diff --git a/.editorconfig b/.editorconfig index a4b824b5d5..f1084288e5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,3 +6,9 @@ end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 + +[*.ps1] +indent_size = 4 +indent_style = space +insert_final_newline = = true +charset = utf-8-bom diff --git a/.gitattributes b/.gitattributes index 5e6fc3ef52..e11419ebbf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ * text=auto *.bicep text eol=lf +*.ps1 text eol=lf +*.sh text eol=lf \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d591b73c4e..31e04d58e8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,6 +3,8 @@ /scripts/ @Azure/bicep-admins @Azure/avm-core-team-technical-bicep /avm/ @Azure/avm-core-team-technical-bicep /avm/utilities/ @Azure/avm-core-team-technical-bicep +/avm/ptn/aca-lza/hosting-environment/ @Azure/avm-ptn-acalza-hostingenvironment-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/ai-platform/baseline/ @Azure/avm-ptn-aiplatform-baseline-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/authorization/policy-assignment/ @Azure/avm-ptn-authorization-policyassignment-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/authorization/resource-role-assignment/ @Azure/avm-ptn-authorization-resourceroleassignment-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-module-reviewers-bicep @@ -10,6 +12,7 @@ #/avm/ptn/avd-lza/management-plane/ @Azure/avm-ptn-avd-lza-managementplane-module-owners-bicep @Azure/avm-module-reviewers-bicep #/avm/ptn/avd-lza/networking/ @Azure/avm-ptn-avd-lza-networking-module-owners-bicep @Azure/avm-module-reviewers-bicep #/avm/ptn/avd-lza/session-hosts/ @Azure/avm-ptn-avd-lza-sessionhosts-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/azd/container-apps/ @Azure/avm-ptn-azd-containerapps-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/deployment-script/import-image-to-acr/ @Azure/avm-ptn-deploymentscript-importimagetoacr-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/finops-toolkit/finops-hub/ @Azure/avm-ptn-finopstoolkit-finopshub-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/lz/sub-vending/ @Azure/avm-ptn-lz-subvending-module-owners-bicep @Azure/avm-module-reviewers-bicep @@ -151,4 +154,4 @@ /avm/res/web/serverfarm/ @Azure/avm-res-web-serverfarm-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/web/site/ @Azure/avm-res-web-site-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/web/static-site/ @Azure/avm-res-web-staticsite-module-owners-bicep @Azure/avm-module-reviewers-bicep -*avm.core.team.tests.ps1 @Azure/avm-core-team-technical-bicep \ No newline at end of file +*avm.core.team.tests.ps1 @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index c125c04c55..ef63ce6e6e 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -38,6 +38,8 @@ body: description: Which existing AVM module is this issue related to? options: - "" + - "avm/ptn/aca-lza/hosting-environment" + - "avm/ptn/ai-platform/baseline" - "avm/ptn/authorization/policy-assignment" - "avm/ptn/authorization/resource-role-assignment" - "avm/ptn/authorization/role-assignment" @@ -45,6 +47,7 @@ body: # - "avm/ptn/avd-lza/management-plane" # - "avm/ptn/avd-lza/networking" # - "avm/ptn/avd-lza/session-hosts" + - "avm/ptn/azd/container-apps" - "avm/ptn/deployment-script/import-image-to-acr" - "avm/ptn/finops-toolkit/finops-hub" - "avm/ptn/lz/sub-vending" diff --git a/.github/actions/templates/avm-validateModuleDeployment/action.yml b/.github/actions/templates/avm-validateModuleDeployment/action.yml index b6e1145f7d..715a2f642b 100644 --- a/.github/actions/templates/avm-validateModuleDeployment/action.yml +++ b/.github/actions/templates/avm-validateModuleDeployment/action.yml @@ -208,11 +208,35 @@ runs: AdditionalParameters = @{} } + # Add custom parameters as needed if($moduleTemplatePossibleParameters -contains 'resourceLocation') { $functionInput.AdditionalParameters += @{ resourceLocation = '${{ steps.get-resource-location.outputs.resourceLocation }}' } } + if($moduleTemplatePossibleParameters -contains 'baseTime') { + $functionInput.AdditionalParameters += @{ + baseTime = (Get-Date).ToString('u') # Setting base time explicitly so that any potential retry will reuse the same timestamp + } + } + + # Fetch & add custom secrets, if any + # ----------------------------------- + $keyVaultName = "${{ env.CI_KEY_VAULT_NAME }}" + if(-not [String]::IsNullOrEmpty($keyVaultName)) { + # Note: This action requires at least 'Key Vault Secrets User' permissions + $customKeyVaultSecrets = Get-AzKeyVaultSecret -VaultName $keyVaultName | Where-Object { $_.Name -match '^CI-.+' } + + foreach($customSecret in $customKeyVaultSecrets) { + $formattedName = $customSecret.Name -replace '^CI-' # e.g. 'CI-mySecret' -> 'mySecret' + if($moduleTemplatePossibleParameters -contains $formattedName) { + Write-Verbose ('Setting value for parameter [{0}]' -f $formattedName) -Verbose + $functionInput.AdditionalParameters += @{ + $formattedName = (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $customSecret.Name).SecretValue + } + } + } + } Write-Verbose 'Invoke task with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose @@ -269,6 +293,29 @@ runs: resourceLocation = '${{ steps.get-resource-location.outputs.resourceLocation }}' } } + if($moduleTemplatePossibleParameters -contains 'baseTime') { + $functionInput.AdditionalParameters += @{ + baseTime = (Get-Date).ToString('u') # Setting base time explicitly so that any potential retry will reuse the same timestamp + } + } + + # Fetch & add custom secrets, if any + # ----------------------------------- + $keyVaultName = "${{ env.CI_KEY_VAULT_NAME }}" + if(-not [String]::IsNullOrEmpty($keyVaultName)) { + # Note: This action requires at least 'Key Vault Secrets User' permissions + $customKeyVaultSecrets = Get-AzKeyVaultSecret -VaultName $keyVaultName | Where-Object { $_.Name -match '^CI-.+' } + + foreach($customSecret in $customKeyVaultSecrets) { + $formattedName = $customSecret.Name -replace '^CI-' # e.g. 'CI-mySecret' -> 'mySecret' + if($moduleTemplatePossibleParameters -contains $formattedName) { + Write-Verbose ('Setting value for parameter [{0}]' -f $formattedName) -Verbose + $functionInput.AdditionalParameters += @{ + $formattedName = (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $customSecret.Name).SecretValue + } + } + } + } Write-Verbose 'Invoke task with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose diff --git a/.github/workflows/avm.ptn.aca-lza.hosting-environment.yml b/.github/workflows/avm.ptn.aca-lza.hosting-environment.yml new file mode 100644 index 0000000000..d7f47a87e5 --- /dev/null +++ b/.github/workflows/avm.ptn.aca-lza.hosting-environment.yml @@ -0,0 +1,90 @@ +name: "avm.ptn.aca-lza.hosting-environment" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + default: 'northeurope' + push: + branches: + - main + - avm-ptn-acalza-hostingenvironment + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.aca-lza.hosting-environment.yml" + - "avm/ptn/aca-lza/hosting-environment/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/aca-lza/hosting-environment" + workflowPath: ".github/workflows/avm.ptn.aca-lza.hosting-environment.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/.github/workflows/avm.ptn.ai-platform.baseline.yml b/.github/workflows/avm.ptn.ai-platform.baseline.yml new file mode 100644 index 0000000000..d6317ad70b --- /dev/null +++ b/.github/workflows/avm.ptn.ai-platform.baseline.yml @@ -0,0 +1,88 @@ +name: "avm.ptn.ai-platform.baseline" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.ai-platform.baseline.yml" + - "avm/ptn/ai-platform/baseline/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/ai-platform/baseline" + workflowPath: ".github/workflows/avm.ptn.ai-platform.baseline.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath }}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath }}" + secrets: inherit diff --git a/.github/workflows/avm.ptn.azd.container-apps.yml b/.github/workflows/avm.ptn.azd.container-apps.yml new file mode 100644 index 0000000000..0759231151 --- /dev/null +++ b/.github/workflows/avm.ptn.azd.container-apps.yml @@ -0,0 +1,88 @@ +name: "avm.ptn.azd.container-apps" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.azd.container-apps.yml" + - "avm/ptn/azd/container-apps/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/azd/container-apps" + workflowPath: ".github/workflows/avm.ptn.azd.container-apps.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/.github/workflows/avm.ptn.lz.sub-vending.yml b/.github/workflows/avm.ptn.lz.sub-vending.yml index 94bdd941f4..061f26242c 100644 --- a/.github/workflows/avm.ptn.lz.sub-vending.yml +++ b/.github/workflows/avm.ptn.lz.sub-vending.yml @@ -27,6 +27,7 @@ on: - ".github/workflows/avm.ptn.lz.sub-vending" - "avm/ptn/lz/sub-vending/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.container-service.managed-cluster.yml b/.github/workflows/avm.res.container-service.managed-cluster.yml index e8c992b399..aae1ec01f4 100644 --- a/.github/workflows/avm.res.container-service.managed-cluster.yml +++ b/.github/workflows/avm.res.container-service.managed-cluster.yml @@ -46,7 +46,7 @@ jobs: # Initialize pipeline # ########################### job_initialize_pipeline: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest name: "Initialize pipeline" steps: - name: "Checkout" diff --git a/.github/workflows/avm.res.web.hosting-environment.yml b/.github/workflows/avm.res.web.hosting-environment.yml index bd38a4d590..5ae62e6a11 100644 --- a/.github/workflows/avm.res.web.hosting-environment.yml +++ b/.github/workflows/avm.res.web.hosting-environment.yml @@ -46,7 +46,7 @@ jobs: # Initialize pipeline # ########################### job_initialize_pipeline: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest name: "Initialize pipeline" steps: - name: "Checkout" diff --git a/.github/workflows/avm.template.module.yml b/.github/workflows/avm.template.module.yml index efb117c5f6..55bbbc0abd 100644 --- a/.github/workflows/avm.template.module.yml +++ b/.github/workflows/avm.template.module.yml @@ -25,6 +25,7 @@ env: ARM_MGMTGROUP_ID: "${{ secrets.ARM_MGMTGROUP_ID }}" ARM_TENANT_ID: "${{ secrets.ARM_TENANT_ID }}" TOKEN_NAMEPREFIX: "${{ secrets.TOKEN_NAMEPREFIX }}" + CI_KEY_VAULT_NAME: "${{ vars.CI_KEY_VAULT_NAME }}" jobs: ######################### diff --git a/.github/workflows/platform.ci-tests.yml b/.github/workflows/platform.ci-tests.yml new file mode 100644 index 0000000000..44f509c72d --- /dev/null +++ b/.github/workflows/platform.ci-tests.yml @@ -0,0 +1,100 @@ +name: ".Platform - Run CI tests" + +on: + workflow_dispatch: + inputs: + testFileRegex: + type: string + description: "The regex of the test file(s) to run" + required: false + default: '.*' + push: + branches: + - main + paths: + - ".github/workflows/platform.ci-tests.yml" + - "avm/utilities/**" + - "!*/**/README.md" + schedule: + - cron: "0 0 * * Sun" # Every Sunday + +env: + workflowPath: ".github/workflows/platform.ci-tests.yml" + + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + + ############### + # Removal # + ############### + job_run_tests: + runs-on: ubuntu-latest + name: "Run CI tests" + needs: + - job_initialize_pipeline + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set environment + uses: ./.github/actions/templates/avm-setEnvironment + + - name: Run CI tests + id: pester_run_step + uses: azure/powershell@v2 + with: + inlineScript: | + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'tests' 'Test-CI.ps1') + + $functionInput = @{ + RepoRootPath = $env:GITHUB_WORKSPACE + BranchName = $env:GITHUB_REF + GitHubRepository = $env:GITHUB_REPOSITORY + TestFileRegex = '${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).testFileRegex }}' + } + + Write-Verbose "Invoke task with" -Verbose + Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose + + $outputsFilePath = Test-CI @functionInput + + Write-Output ('{0}={1}' -f 'formattedPesterResultsPath', $outputsFilePath) >> $env:GITHUB_OUTPUT + azPSVersion: "latest" + + - name: "Output to GitHub job summaries" + if: always() + shell: pwsh + run: | + # Grouping task logs + Write-Output '::group::Output to GitHub job summaries' + + $mdPesterOutputFilePath = '${{ steps.pester_run_step.outputs.formattedPesterResultsPath }}' + + if (-not (Test-Path $mdPesterOutputFilePath)) { + Write-Warning ('Input file [{0}] not found. Please check if the previous task threw an error and try again.' -f $mdPesterOutputFilePath) + } else { + Get-Content $mdPesterOutputFilePath >> $env:GITHUB_STEP_SUMMARY + Write-Verbose ('Successfully printed out file [{0}] to Job Summaries' -f $mdPesterOutputFilePath) -Verbose + } + Write-Output '::endgroup::' \ No newline at end of file diff --git a/.github/workflows/platform.deployment.history.cleanup.yml b/.github/workflows/platform.deployment.history.cleanup.yml index 064514271d..d098f1cae6 100644 --- a/.github/workflows/platform.deployment.history.cleanup.yml +++ b/.github/workflows/platform.deployment.history.cleanup.yml @@ -48,8 +48,11 @@ jobs: # Removal # ############### job_cleanup_subscription_deployments: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest name: "Remove Subscription deployments" + environment: avm-validation + permissions: + id-token: write # For OIDC needs: - job_initialize_pipeline if: ${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).handleSubscriptionScope == 'true' }} @@ -62,10 +65,17 @@ jobs: - name: Set environment uses: ./.github/actions/templates/avm-setEnvironment + # [Azure login] task(s) + # ------------------------------ + # Supports both OIDC and service principal with secret + # 'creds' will be ignored if 'client-id', 'subscription-id' or 'tenant-id' is set - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.VALIDATE_CLIENT_ID }} + tenant-id: ${{ secrets.VALIDATE_TENANT_ID }} + subscription-id: ${{ secrets.VALIDATE_SUBSCRIPTION_ID }} enable-AzPSSession: true - name: Remove deployments @@ -87,8 +97,11 @@ jobs: azPSVersion: "latest" job_cleanup_managementGroup_deployments: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest name: "Remove Management Group deployments" + environment: avm-validation + permissions: + id-token: write # For OIDC needs: - job_initialize_pipeline if: ${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).handleManagementGroupScope == 'true' }} @@ -101,10 +114,17 @@ jobs: - name: Set environment uses: ./.github/actions/templates/avm-setEnvironment + # [Azure login] task(s) + # ------------------------------ + # Supports both OIDC and service principal with secret + # 'creds' will be ignored if 'client-id', 'subscription-id' or 'tenant-id' is set - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} + client-id: ${{ secrets.VALIDATE_CLIENT_ID }} + tenant-id: ${{ secrets.VALIDATE_TENANT_ID }} + subscription-id: ${{ secrets.VALIDATE_SUBSCRIPTION_ID }} enable-AzPSSession: true - name: Remove deployments @@ -114,13 +134,18 @@ jobs: # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'deploymentRemoval' 'Clear-ManagementGroupDeploymentHistory.ps1') - $functionInput = @{ - ManagementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - maxDeploymentRetentionInDays = '${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).maxDeploymentRetentionInDays }}' - } + $mgmtGroupIdInput = '${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).customManagementGroupId }}' - Write-Verbose "Invoke task with" -Verbose - Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose + foreach($mgmtGroupId in @('${{ secrets.ARM_MGMTGROUP_ID }}', 'bicep-lz-vending-automation-child')) { + Write-Verbose "Processing mgmtGroupId [$mgmtGroupId]" -Verbose + $functionInput = @{ + ManagementGroupId = $mgmtGroupId + maxDeploymentRetentionInDays = '${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).maxDeploymentRetentionInDays }}' + } - Clear-ManagementGroupDeploymentHistory @functionInput + Write-Verbose "Invoke task with" -Verbose + Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose + + Clear-ManagementGroupDeploymentHistory @functionInput + } azPSVersion: "latest" diff --git a/.github/workflows/platform.ossf-scorecard.yml b/.github/workflows/platform.ossf-scorecard.yml index f0f6af0845..5c37e5fc09 100644 --- a/.github/workflows/platform.ossf-scorecard.yml +++ b/.github/workflows/platform.ossf-scorecard.yml @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: SARIF file path: results.sarif @@ -68,6 +68,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: sarif_file: results.sarif diff --git a/.github/workflows/platform.publish-module-index-json.yml b/.github/workflows/platform.publish-module-index-json.yml index 1e05a8fbbb..b6a510c1f0 100644 --- a/.github/workflows/platform.publish-module-index-json.yml +++ b/.github/workflows/platform.publish-module-index-json.yml @@ -4,11 +4,20 @@ on: schedule: - cron: 45 11 * * * # Run daily at 3:45 AM PST workflow_dispatch: + inputs: + regenIndexFromBRM: + type: boolean + description: "regenIndexFromBRM | Regenerate the moduleIndex.json file from scratch based on what is in BRM at the time of the run, instead of integrating it with its previous version." + required: false + default: false permissions: id-token: write contents: read +env: + workflowPath: ".github/workflows/avm.res.app.job.yml" + jobs: upload-index-data: runs-on: ubuntu-latest @@ -17,7 +26,11 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 // Needed to fetch all history and tags - + - name: "Set input parameter``s to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" - name: Log in to Azure uses: azure/login@v2 with: @@ -49,15 +62,19 @@ jobs: ErrorAction = 'Continue' } - Write-Verbose "Invoke task with" -Verbose + if ('${{ (fromJson(steps.get-workflow-param.outputs.workflowInput)).regenIndexFromBRM }}' -eq $true ) { + $functionInput['doNotMergeWithLastModuleIndexJsonFileVersion'] = $true + } + + Write-Verbose 'Invoke task with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - if(-not (Invoke-AvmJsonModuleIndexGeneration @functionInput)) { + if (-not (Invoke-AvmJsonModuleIndexGeneration @functionInput)) { Write-Output ('{0}={1}' -f 'anyErrorsOccurred', $true) >> $env:GITHUB_ENV } - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.3.5 with: name: publish-module-index-json-artifacts path: | diff --git a/.github/workflows/platform.set-avm-github-pr-labels.yml b/.github/workflows/platform.set-avm-github-pr-labels.yml new file mode 100644 index 0000000000..e273937a9d --- /dev/null +++ b/.github/workflows/platform.set-avm-github-pr-labels.yml @@ -0,0 +1,42 @@ +# Workflow for adding labels to newly created PRs +name: .Platform - Set AVM GitHub PR labels + +on: + pull_request_target: + types: [opened, ready_for_review] + +jobs: + SetAvmGitHubPrLabels: + if: github.repository == 'Azure/bicep-registry-modules' + name: Set-AvmGitHubPrLabels + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} + - name: "Run scripts" + shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGitHubPrLabels.ps1') + + $functionInput = @{ + RepoRoot = $env:GITHUB_WORKSPACE + Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + PrUrl = "${{ github.event.pull_request.url }}" + } + + Write-Verbose "Invoke task with" -Verbose + Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose + + Set-AvmGitHubPrLabels @functionInput -Verbose # -WhatIf diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c83e26348e..b36c59714e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["esbenp.prettier-vscode"] + "recommendations": [ + "esbenp.prettier-vscode", + "editorconfig.editorconfig", + "ms-vscode.powershell" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 9d774d0efc..0e02c76f6c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,4 +29,4 @@ "files.insertFinalNewline": true, "editor.detectIndentation": false // VS Code will not detect indentation/tab/space from the file and use settings editor.insertSpaces and editor.tabSize instead } -} +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/README.md b/avm/ptn/aca-lza/hosting-environment/README.md new file mode 100644 index 0000000000..04ba6bd2e7 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/README.md @@ -0,0 +1,845 @@ +# Container Apps Landing Zone Accelerator `[AcaLza/HostingEnvironment]` + +This Azure Container Apps pattern module represents an Azure Container Apps deployment aligned with the cloud adoption framework + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.App/containerApps` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-05-01/containerApps) | +| `Microsoft.App/managedEnvironments` | [2023-11-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-11-02-preview/managedEnvironments) | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Automanage/configurationProfileAssignments` | [2022-05-04](https://learn.microsoft.com/en-us/azure/templates) | +| `Microsoft.Cdn/profiles` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles) | +| `Microsoft.Cdn/profiles/afdEndpoints` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/afdEndpoints) | +| `Microsoft.Cdn/profiles/afdEndpoints/routes` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/afdEndpoints/routes) | +| `Microsoft.Cdn/profiles/customDomains` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/customDomains) | +| `Microsoft.Cdn/profiles/endpoints` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2021-06-01/profiles/endpoints) | +| `Microsoft.Cdn/profiles/endpoints/origins` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2021-06-01/profiles/endpoints/origins) | +| `Microsoft.Cdn/profiles/originGroups` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/originGroups) | +| `Microsoft.Cdn/profiles/originGroups/origins` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/originGroups/origins) | +| `Microsoft.Cdn/profiles/ruleSets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets) | +| `Microsoft.Cdn/profiles/ruleSets/rules` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets/rules) | +| `Microsoft.Cdn/profiles/secrets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/secrets) | +| `Microsoft.Compute/virtualMachines` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-09-01/virtualMachines) | +| `Microsoft.Compute/virtualMachines/extensions` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines/extensions) | +| `Microsoft.ContainerRegistry/registries` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries) | +| `Microsoft.ContainerRegistry/registries/cacheRules` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/cacheRules) | +| `Microsoft.ContainerRegistry/registries/replications` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/replications) | +| `Microsoft.ContainerRegistry/registries/webhooks` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/webhooks) | +| `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | +| `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | +| `Microsoft.Insights/components` | [2020-02-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-02-02/components) | +| `microsoft.insights/components/linkedStorageAccounts` | [2020-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2020-03-01-preview/components/linkedStorageAccounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | +| `Microsoft.KeyVault/vaults/keys` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/keys) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | +| `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | +| `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | +| `Microsoft.Maintenance/maintenanceConfigurations` | [2023-10-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/maintenanceConfigurations) | +| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities) | +| `Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities/federatedIdentityCredentials) | +| `Microsoft.Network/applicationGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/applicationGateways) | +| `Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/ApplicationGatewayWebApplicationFirewallPolicies) | +| `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | +| `Microsoft.Network/networkSecurityGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkSecurityGroups) | +| `Microsoft.Network/networkSecurityGroups/securityRules` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkSecurityGroups/securityRules) | +| `Microsoft.Network/privateDnsZones` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones) | +| `Microsoft.Network/privateDnsZones/A` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | +| `Microsoft.Network/privateDnsZones/AAAA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | +| `Microsoft.Network/privateDnsZones/CNAME` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | +| `Microsoft.Network/privateDnsZones/MX` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | +| `Microsoft.Network/privateDnsZones/PTR` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | +| `Microsoft.Network/privateDnsZones/SOA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | +| `Microsoft.Network/privateDnsZones/SRV` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | +| `Microsoft.Network/privateDnsZones/TXT` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateLinkServices` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateLinkServices) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/routeTables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/routeTables) | +| `Microsoft.Network/virtualNetworks` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks/virtualNetworkPeerings) | +| `Microsoft.OperationalInsights/workspaces` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces) | +| `Microsoft.OperationalInsights/workspaces/dataExports` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataExports) | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | +| `Microsoft.OperationalInsights/workspaces/tables` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces/tables) | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | +| `Microsoft.Resources/resourceGroups` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/resourceGroups) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/aca-lza/hosting-environment:`. + +- [Using only defaults.](#example-1-using-only-defaults) +- [Using a hub and spoke deployment.](#example-2-using-a-hub-and-spoke-deployment) +- [Using all the available options in WAF aligned values.](#example-3-using-all-the-available-options-in-waf-aligned-values) + +### Example 1: _Using only defaults._ + +This instance deploys the module with the minimum set of required parameters. + + +
+ +via Bicep module + +```bicep +module hostingEnvironment 'br/public:avm/ptn/aca-lza/hosting-environment:' = { + name: 'hostingEnvironmentDeployment' + params: { + // Required parameters + applicationGatewayCertificateKeyName: 'appgwcert' + enableApplicationInsights: true + enableDaprInstrumentation: false + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + vmAdminPassword: '' + vmAdminUsername: 'vmadmin' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmSize: 'Standard_B1s' + // Non-required parameters + location: '' + tags: { + environment: 'test' + } + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + workloadName: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "applicationGatewayCertificateKeyName": { + "value": "appgwcert" + }, + "enableApplicationInsights": { + "value": true + }, + "enableDaprInstrumentation": { + "value": false + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "value": "10.1.3.0/24" + }, + "spokeInfraSubnetAddressPrefix": { + "value": "10.1.0.0/23" + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "value": "10.1.2.0/27" + }, + "spokeVNetAddressPrefixes": { + "value": [ + "10.1.0.0/22" + ] + }, + "vmAdminPassword": { + "value": "" + }, + "vmAdminUsername": { + "value": "vmadmin" + }, + "vmJumpBoxSubnetAddressPrefix": { + "value": "10.1.2.32/27" + }, + "vmLinuxSshAuthorizedKey": { + "value": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain" + }, + "vmSize": { + "value": "Standard_B1s" + }, + // Non-required parameters + "location": { + "value": "" + }, + "tags": { + "value": { + "environment": "test" + } + }, + "vmAuthenticationType": { + "value": "sshPublicKey" + }, + "vmJumpboxOSType": { + "value": "linux" + }, + "workloadName": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using a hub and spoke deployment._ + +This instance deploys the module including a Hub to peer to. + + +

+ +via Bicep module + +```bicep +module hostingEnvironment 'br/public:avm/ptn/aca-lza/hosting-environment:' = { + name: 'hostingEnvironmentDeployment' + params: { + // Required parameters + applicationGatewayCertificateKeyName: 'appgwcert' + enableApplicationInsights: true + enableDaprInstrumentation: false + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + vmAdminPassword: '' + vmAdminUsername: 'vmadmin' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmSize: 'Standard_B1s' + // Non-required parameters + deployZoneRedundantResources: true + enableDdosProtection: true + environment: 'dev' + exposeContainerAppsWith: 'applicationGateway' + hubVirtualNetworkResourceId: '' + location: '' + networkApplianceIpAddress: '' + storageAccountType: 'Premium_LRS' + tags: { + environment: 'test' + } + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + workloadName: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "applicationGatewayCertificateKeyName": { + "value": "appgwcert" + }, + "enableApplicationInsights": { + "value": true + }, + "enableDaprInstrumentation": { + "value": false + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "value": "10.1.3.0/24" + }, + "spokeInfraSubnetAddressPrefix": { + "value": "10.1.0.0/23" + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "value": "10.1.2.0/27" + }, + "spokeVNetAddressPrefixes": { + "value": [ + "10.1.0.0/22" + ] + }, + "vmAdminPassword": { + "value": "" + }, + "vmAdminUsername": { + "value": "vmadmin" + }, + "vmJumpBoxSubnetAddressPrefix": { + "value": "10.1.2.32/27" + }, + "vmLinuxSshAuthorizedKey": { + "value": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain" + }, + "vmSize": { + "value": "Standard_B1s" + }, + // Non-required parameters + "deployZoneRedundantResources": { + "value": true + }, + "enableDdosProtection": { + "value": true + }, + "environment": { + "value": "dev" + }, + "exposeContainerAppsWith": { + "value": "applicationGateway" + }, + "hubVirtualNetworkResourceId": { + "value": "" + }, + "location": { + "value": "" + }, + "networkApplianceIpAddress": { + "value": "" + }, + "storageAccountType": { + "value": "Premium_LRS" + }, + "tags": { + "value": { + "environment": "test" + } + }, + "vmAuthenticationType": { + "value": "sshPublicKey" + }, + "vmJumpboxOSType": { + "value": "linux" + }, + "workloadName": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Using all the available options in WAF aligned values._ + +This instance deploys the module with the all the available parameters in WAF aligned values. + + +

+ +via Bicep module + +```bicep +module hostingEnvironment 'br/public:avm/ptn/aca-lza/hosting-environment:' = { + name: 'hostingEnvironmentDeployment' + params: { + // Required parameters + applicationGatewayCertificateKeyName: 'appgwcert' + enableApplicationInsights: true + enableDaprInstrumentation: false + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + vmAdminPassword: '' + vmAdminUsername: 'vmadmin' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmSize: 'Standard_B1s' + // Non-required parameters + deployZoneRedundantResources: true + enableDdosProtection: true + environment: 'dev' + exposeContainerAppsWith: 'applicationGateway' + location: '' + storageAccountType: 'Premium_LRS' + tags: { + environment: 'test' + } + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + workloadName: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "applicationGatewayCertificateKeyName": { + "value": "appgwcert" + }, + "enableApplicationInsights": { + "value": true + }, + "enableDaprInstrumentation": { + "value": false + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "value": "10.1.3.0/24" + }, + "spokeInfraSubnetAddressPrefix": { + "value": "10.1.0.0/23" + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "value": "10.1.2.0/27" + }, + "spokeVNetAddressPrefixes": { + "value": [ + "10.1.0.0/22" + ] + }, + "vmAdminPassword": { + "value": "" + }, + "vmAdminUsername": { + "value": "vmadmin" + }, + "vmJumpBoxSubnetAddressPrefix": { + "value": "10.1.2.32/27" + }, + "vmLinuxSshAuthorizedKey": { + "value": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain" + }, + "vmSize": { + "value": "Standard_B1s" + }, + // Non-required parameters + "deployZoneRedundantResources": { + "value": true + }, + "enableDdosProtection": { + "value": true + }, + "environment": { + "value": "dev" + }, + "exposeContainerAppsWith": { + "value": "applicationGateway" + }, + "location": { + "value": "" + }, + "storageAccountType": { + "value": "Premium_LRS" + }, + "tags": { + "value": { + "environment": "test" + } + }, + "vmAuthenticationType": { + "value": "sshPublicKey" + }, + "vmJumpboxOSType": { + "value": "linux" + }, + "workloadName": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationGatewayCertificateKeyName`](#parameter-applicationgatewaycertificatekeyname) | string | The name of the certificate key to use for Application Gateway certificate. | +| [`enableApplicationInsights`](#parameter-enableapplicationinsights) | bool | Enable or disable the createion of Application Insights. | +| [`enableDaprInstrumentation`](#parameter-enabledaprinstrumentation) | bool | Enable or disable Dapr Application Instrumentation Key used for Dapr telemetry. If Application Insights is not enabled, this parameter is ignored. | +| [`spokeApplicationGatewaySubnetAddressPrefix`](#parameter-spokeapplicationgatewaysubnetaddressprefix) | string | CIDR of the Spoke Application Gateway Subnet. | +| [`spokeInfraSubnetAddressPrefix`](#parameter-spokeinfrasubnetaddressprefix) | string | CIDR of the Spoke Infrastructure Subnet. | +| [`spokePrivateEndpointsSubnetAddressPrefix`](#parameter-spokeprivateendpointssubnetaddressprefix) | string | CIDR of the Spoke Private Endpoints Subnet. | +| [`spokeVNetAddressPrefixes`](#parameter-spokevnetaddressprefixes) | array | CIDR of the Spoke Virtual Network. | +| [`vmAdminPassword`](#parameter-vmadminpassword) | securestring | The password to use for the virtual machine. | +| [`vmAdminUsername`](#parameter-vmadminusername) | string | The username to use for the virtual machine. | +| [`vmJumpBoxSubnetAddressPrefix`](#parameter-vmjumpboxsubnetaddressprefix) | string | CIDR to use for the virtual machine subnet. | +| [`vmLinuxSshAuthorizedKey`](#parameter-vmlinuxsshauthorizedkey) | securestring | The SSH public key to use for the virtual machine. | +| [`vmSize`](#parameter-vmsize) | string | The size of the virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationGatewayFqdn`](#parameter-applicationgatewayfqdn) | string | The FQDN of the Application Gateway. Required and must match if the TLS Certificate is provided. Default is empty. | +| [`base64Certificate`](#parameter-base64certificate) | string | The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault and assigned to the Application Gateway listener. Default is empty. | +| [`bastionResourceId`](#parameter-bastionresourceid) | string | The resource ID of the bastion host. If set, the spoke virtual network will be peered with the hub virtual network and the bastion host will be allowed to connect to the jump box. Default is empty. | +| [`deploySampleApplication`](#parameter-deploysampleapplication) | bool | Deploy sample application to the container apps environment. Default is false. | +| [`deployZoneRedundantResources`](#parameter-deployzoneredundantresources) | bool | Default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false. Default is true. | +| [`enableDdosProtection`](#parameter-enableddosprotection) | bool | DDoS protection mode. see https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus. Default is "false". | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`environment`](#parameter-environment) | string | The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long. Default is "test". | +| [`exposeContainerAppsWith`](#parameter-exposecontainerappswith) | string | Specify the way container apps is going to be exposed. Options are applicationGateway or frontDoor. Default is "applicationGateway". | +| [`hubVirtualNetworkResourceId`](#parameter-hubvirtualnetworkresourceid) | string | The resource ID of the hub virtual network. If set, the spoke virtual network will be peered with the hub virtual network. Default is empty. | +| [`location`](#parameter-location) | string | The location of the Azure Container Apps deployment. Default is the location of the deployment location. | +| [`networkApplianceIpAddress`](#parameter-networkapplianceipaddress) | string | If set, the spoke virtual network will be peered with the hub virtual network and egres traffic will be routed through the network appliance. Default is empty. | +| [`spokeResourceGroupName`](#parameter-spokeresourcegroupname) | string | The name of the resource group to create the resources in. If set, it overrides the name generated by the template. Default is empty. | +| [`storageAccountType`](#parameter-storageaccounttype) | string | The storage account type to use for the jump box. Defaults to Standard_LRS. | +| [`tags`](#parameter-tags) | object | Tags related to the Azure Container Apps deployment. Default is empty. | +| [`vmAuthenticationType`](#parameter-vmauthenticationtype) | string | Type of authentication to use on the Virtual Machine. SSH key is recommended. Default is "password". | +| [`vmJumpboxOSType`](#parameter-vmjumpboxostype) | string | The operating system type of the virtual machine. Default is "none" which results in no VM deployment. Default is "none". | +| [`workloadName`](#parameter-workloadname) | string | The name of the workload that is being deployed. Up to 10 characters long. | + +### Parameter: `applicationGatewayCertificateKeyName` + +The name of the certificate key to use for Application Gateway certificate. + +- Required: Yes +- Type: string + +### Parameter: `enableApplicationInsights` + +Enable or disable the createion of Application Insights. + +- Required: Yes +- Type: bool + +### Parameter: `enableDaprInstrumentation` + +Enable or disable Dapr Application Instrumentation Key used for Dapr telemetry. If Application Insights is not enabled, this parameter is ignored. + +- Required: Yes +- Type: bool + +### Parameter: `spokeApplicationGatewaySubnetAddressPrefix` + +CIDR of the Spoke Application Gateway Subnet. + +- Required: Yes +- Type: string + +### Parameter: `spokeInfraSubnetAddressPrefix` + +CIDR of the Spoke Infrastructure Subnet. + +- Required: Yes +- Type: string + +### Parameter: `spokePrivateEndpointsSubnetAddressPrefix` + +CIDR of the Spoke Private Endpoints Subnet. + +- Required: Yes +- Type: string + +### Parameter: `spokeVNetAddressPrefixes` + +CIDR of the Spoke Virtual Network. + +- Required: Yes +- Type: array + +### Parameter: `vmAdminPassword` + +The password to use for the virtual machine. + +- Required: Yes +- Type: securestring + +### Parameter: `vmAdminUsername` + +The username to use for the virtual machine. + +- Required: Yes +- Type: string + +### Parameter: `vmJumpBoxSubnetAddressPrefix` + +CIDR to use for the virtual machine subnet. + +- Required: Yes +- Type: string + +### Parameter: `vmLinuxSshAuthorizedKey` + +The SSH public key to use for the virtual machine. + +- Required: Yes +- Type: securestring + +### Parameter: `vmSize` + +The size of the virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information. + +- Required: Yes +- Type: string + +### Parameter: `applicationGatewayFqdn` + +The FQDN of the Application Gateway. Required and must match if the TLS Certificate is provided. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `base64Certificate` + +The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault and assigned to the Application Gateway listener. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `bastionResourceId` + +The resource ID of the bastion host. If set, the spoke virtual network will be peered with the hub virtual network and the bastion host will be allowed to connect to the jump box. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `deploySampleApplication` + +Deploy sample application to the container apps environment. Default is false. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `deployZoneRedundantResources` + +Default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false. Default is true. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enableDdosProtection` + +DDoS protection mode. see https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus. Default is "false". + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `environment` + +The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long. Default is "test". + +- Required: No +- Type: string +- Default: `'test'` + +### Parameter: `exposeContainerAppsWith` + +Specify the way container apps is going to be exposed. Options are applicationGateway or frontDoor. Default is "applicationGateway". + +- Required: No +- Type: string +- Default: `'applicationGateway'` +- Allowed: + ```Bicep + [ + 'applicationGateway' + 'frontDoor' + ] + ``` + +### Parameter: `hubVirtualNetworkResourceId` + +The resource ID of the hub virtual network. If set, the spoke virtual network will be peered with the hub virtual network. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `location` + +The location of the Azure Container Apps deployment. Default is the location of the deployment location. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `networkApplianceIpAddress` + +If set, the spoke virtual network will be peered with the hub virtual network and egres traffic will be routed through the network appliance. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `spokeResourceGroupName` + +The name of the resource group to create the resources in. If set, it overrides the name generated by the template. Default is empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `storageAccountType` + +The storage account type to use for the jump box. Defaults to Standard_LRS. + +- Required: No +- Type: string +- Default: `'Standard_LRS'` + +### Parameter: `tags` + +Tags related to the Azure Container Apps deployment. Default is empty. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `vmAuthenticationType` + +Type of authentication to use on the Virtual Machine. SSH key is recommended. Default is "password". + +- Required: No +- Type: string +- Default: `'password'` +- Allowed: + ```Bicep + [ + 'password' + 'sshPublicKey' + ] + ``` + +### Parameter: `vmJumpboxOSType` + +The operating system type of the virtual machine. Default is "none" which results in no VM deployment. Default is "none". + +- Required: No +- Type: string +- Default: `'none'` +- Allowed: + ```Bicep + [ + 'linux' + 'none' + 'windows' + ] + ``` + +### Parameter: `workloadName` + +The name of the workload that is being deployed. Up to 10 characters long. + +- Required: No +- Type: string +- Default: `'aca-lza'` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `applicationGatewayFqdn` | string | The FQDN of the Azure Application Gateway. | +| `applicationGatewayPublicIp` | string | The public IP address of the Azure Application Gateway. | +| `applicationGatewayResourceId` | string | The resource ID of the Azure Application Gateway. | +| `applicationInsightsName` | string | The name of application Insights instance. | +| `containerAppsEnvironmentName` | string | The name of the container apps environment. | +| `containerAppsEnvironmentResourceId` | string | The resource ID of the container apps environment. | +| `containerRegistryLoginServer` | string | The name of the container registry login server. | +| `containerRegistryName` | string | The name of the container registry. | +| `containerRegistryResourceId` | string | The resource ID of the container registry. | +| `containerRegistryUserAssignedIdentityResourceId` | string | The resource ID of the user assigned managed identity for the container registry to be able to pull images from it. | +| `keyVaultName` | string | The name of the Azure key vault. | +| `keyVaultResourceId` | string | The resource ID of the key vault. | +| `logAnalyticsWorkspaceResourceId` | string | The resource ID of the Log Analytics workspace created in the spoke vnet. | +| `spokeApplicationGatewaySubnetName` | string | The name of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty. | +| `spokeApplicationGatewaySubnetResourceId` | string | The resource ID of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty. | +| `spokeInfraSubnetName` | string | The name of the Spoke Infrastructure Subnet. | +| `spokeInfraSubnetResourceId` | string | The resource ID of the Spoke Infrastructure Subnet. | +| `spokePrivateEndpointsSubnetName` | string | The name of the Spoke Private Endpoints Subnet. | +| `spokeResourceGroupName` | string | The name of the Spoke resource group. | +| `spokeVnetName` | string | The name of the Spoke Virtual Network. | +| `spokeVNetResourceId` | string | The resource ID of the Spoke Virtual Network. | +| `vmJumpBoxName` | string | The name of the jump box virtual machine. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/app/container-app:0.4.0` | Remote reference | +| `br/public:avm/res/app/managed-environment:0.5.1` | Remote reference | +| `br/public:avm/res/cdn/profile:0.3.0` | Remote reference | +| `br/public:avm/res/compute/virtual-machine:0.5.1` | Remote reference | +| `br/public:avm/res/container-registry/registry:0.3.0` | Remote reference | +| `br/public:avm/res/insights/component:0.3.1` | Remote reference | +| `br/public:avm/res/key-vault/vault:0.6.1` | Remote reference | +| `br/public:avm/res/managed-identity/user-assigned-identity:0.2.1` | Remote reference | +| `br/public:avm/res/network/application-gateway-web-application-firewall-policy:0.1.0` | Remote reference | +| `br/public:avm/res/network/application-gateway:0.1.0` | Remote reference | +| `br/public:avm/res/network/network-security-group:0.2.0` | Remote reference | +| `br/public:avm/res/network/private-dns-zone:0.3.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.4.1` | Remote reference | +| `br/public:avm/res/network/route-table:0.2.2` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.1.6` | Remote reference | +| `br/public:avm/res/operational-insights/workspace:0.3.4` | Remote reference | +| `br/public:avm/res/resources/resource-group:0.2.3` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/aca-lza/hosting-environment/main.bicep b/avm/ptn/aca-lza/hosting-environment/main.bicep new file mode 100644 index 0000000000..c1bd3b4879 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/main.bicep @@ -0,0 +1,370 @@ +targetScope = 'subscription' + +metadata name = 'Container Apps Landing Zone Accelerator' +metadata description = 'This Azure Container Apps pattern module represents an Azure Container Apps deployment aligned with the cloud adoption framework' +metadata owner = 'Azure/module-maintainers' + +// ------------------ +// PARAMETERS +// ------------------ +// @description('Required. The id of the subscription to create the Azure Container Apps deployment.') +// param subscriptionId string + +@description('Optional. The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string = 'aca-lza' + +@description('Optional. The resource ID of the hub virtual network. If set, the spoke virtual network will be peered with the hub virtual network. Default is empty.') +param hubVirtualNetworkResourceId string = '' + +@description('Optional. The resource ID of the bastion host. If set, the spoke virtual network will be peered with the hub virtual network and the bastion host will be allowed to connect to the jump box. Default is empty.') +param bastionResourceId string = '' + +@description('Optional. If set, the spoke virtual network will be peered with the hub virtual network and egres traffic will be routed through the network appliance. Default is empty.') +param networkApplianceIpAddress string = '' + +@description('Optional. Tags related to the Azure Container Apps deployment. Default is empty.') +param tags object = {} + +@description('Optional. The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long. Default is "test".') +@maxLength(8) +param environment string = 'test' + +@description('Optional. The location of the Azure Container Apps deployment. Default is the location of the deployment location.') +param location string = deployment().location + +// Jumpbox Virtual Machine +@description('Required. The size of the virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information.') +param vmSize string + +@description('Optional. The storage account type to use for the jump box. Defaults to Standard_LRS.') +param storageAccountType string = 'Standard_LRS' + +@description('Required. The username to use for the virtual machine.') +param vmAdminUsername string + +@description('Required. The password to use for the virtual machine.') +@secure() +param vmAdminPassword string + +@description('Required. The SSH public key to use for the virtual machine.') +@secure() +param vmLinuxSshAuthorizedKey string + +@description('Optional. Type of authentication to use on the Virtual Machine. SSH key is recommended. Default is "password".') +@allowed([ + 'sshPublicKey' + 'password' +]) +param vmAuthenticationType string = 'password' + +@allowed(['linux', 'windows', 'none']) +@description('Optional. The operating system type of the virtual machine. Default is "none" which results in no VM deployment. Default is "none".') +param vmJumpboxOSType string = 'none' + +@description('Required. CIDR to use for the virtual machine subnet.') +param vmJumpBoxSubnetAddressPrefix string + +@description('Optional. The name of the resource group to create the resources in. If set, it overrides the name generated by the template. Default is empty.') +param spokeResourceGroupName string = '' + +@description('Required. CIDR of the Spoke Virtual Network.') +param spokeVNetAddressPrefixes array + +@description('Required. CIDR of the Spoke Infrastructure Subnet.') +param spokeInfraSubnetAddressPrefix string + +@description('Required. CIDR of the Spoke Private Endpoints Subnet.') +param spokePrivateEndpointsSubnetAddressPrefix string + +@description('Required. CIDR of the Spoke Application Gateway Subnet.') +param spokeApplicationGatewaySubnetAddressPrefix string + +@description('Required. Enable or disable the createion of Application Insights.') +param enableApplicationInsights bool + +@description('Required. Enable or disable Dapr Application Instrumentation Key used for Dapr telemetry. If Application Insights is not enabled, this parameter is ignored.') +param enableDaprInstrumentation bool + +@description('Optional. The FQDN of the Application Gateway. Required and must match if the TLS Certificate is provided. Default is empty.') +param applicationGatewayFqdn string = '' + +@description('Optional. The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault and assigned to the Application Gateway listener. Default is empty.') +param base64Certificate string = '' + +@description('Required. The name of the certificate key to use for Application Gateway certificate.') +param applicationGatewayCertificateKeyName string + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false. Default is true.') +param deployZoneRedundantResources bool = true + +// @description('Optional. If true, Azure Policies will be deployed. Default value is true.') +// param deployAzurePolicies bool = true + +@description('Optional. Specify the way container apps is going to be exposed. Options are applicationGateway or frontDoor. Default is "applicationGateway".') +@allowed([ + 'applicationGateway' + 'frontDoor' +]) +param exposeContainerAppsWith string = 'applicationGateway' + +@description('Optional. Deploy sample application to the container apps environment. Default is false.') +param deploySampleApplication bool = false + +@description('Optional. DDoS protection mode. see https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus. Default is "false".') +param enableDdosProtection bool = false + +// ------------------ +// VARIABLES +// ------------------ +var namingRules = json(loadTextContent('modules/naming/naming-rules.jsonc')) +var rgSpokeName = !empty(spokeResourceGroupName) + ? spokeResourceGroupName + : '${namingRules.resourceTypeAbbreviations.resourceGroup}-${workloadName}-spoke-${environment}-${namingRules.regionAbbreviations[toLower(location)]}' + +// ------------------ +// RESOURCES +// ------------------ +@description('User-configured naming rules') +module naming 'modules/naming/naming.module.bicep' = { + scope: resourceGroup(rgSpokeName) + name: take('sharedNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(spoke.outputs.spokeResourceGroupName) + environment: environment + workloadName: workloadName + location: location + } +} + +module spoke 'modules/spoke/deploy.spoke.bicep' = { + name: take('spoke-${deployment().name}-deployment', 64) + scope: subscription() + params: { + spokeResourceGroupName: rgSpokeName + location: location + tags: tags + enableTelemetry: enableTelemetry + environment: environment + //subscriptionId: subscriptionId + workloadName: workloadName + hubVNetId: hubVirtualNetworkResourceId + bastionResourceId: bastionResourceId + spokeApplicationGatewaySubnetAddressPrefix: spokeApplicationGatewaySubnetAddressPrefix + spokeInfraSubnetAddressPrefix: spokeInfraSubnetAddressPrefix + spokePrivateEndpointsSubnetAddressPrefix: spokePrivateEndpointsSubnetAddressPrefix + spokeVNetAddressPrefixes: spokeVNetAddressPrefixes + networkApplianceIpAddress: networkApplianceIpAddress + vmSize: vmSize + vmZone: (deployZoneRedundantResources) ? 2 : 0 + storageAccountType: storageAccountType + vmAdminUsername: vmAdminUsername + vmAdminPassword: vmAdminPassword + vmLinuxSshAuthorizedKey: vmLinuxSshAuthorizedKey + vmJumpboxOSType: vmJumpboxOSType + vmJumpBoxSubnetAddressPrefix: vmJumpBoxSubnetAddressPrefix + vmAuthenticationType: vmAuthenticationType + } +} + +// Policy assignement requires a management group scope +// @description('Assign built-in and custom (container-apps related) policies to the spoke subscription.') +// module policiesDeployment 'modules/policy/policies-assignement.bicep' = if (deployAzurePolicies) { +// name: take('policyAssignments-${deployment().name}', 64) +// params: { +// location: location +// spokeResourceGroupName: spoke.outputs.spokeResourceGroupName +// containerRegistryName: naming.outputs.resourcesNames.containerRegistry +// } +// } + +module supportingServices 'modules/supporting-services/deploy.supporting-services.bicep' = { + name: take('supportingServices-${deployment().name}-deployment', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + spokePrivateEndpointSubnetName: spoke.outputs.spokePrivateEndpointsSubnetName + environment: environment + workloadName: workloadName + spokeVNetId: spoke.outputs.spokeVNetId + hubVNetId: hubVirtualNetworkResourceId + logAnalyticsWorkspaceId: spoke.outputs.logAnalyticsWorkspaceId + } +} + +module containerAppsEnvironment 'modules/container-apps-environment/deploy.aca-environment.bicep' = { + name: take('containerAppsEnvironment-${deployment().name}-deployment', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + environment: environment + workloadName: workloadName + hubVNetId: hubVirtualNetworkResourceId + spokeVNetName: spoke.outputs.spokeVNetName + spokeInfraSubnetName: spoke.outputs.spokeInfraSubnetName + enableApplicationInsights: enableApplicationInsights + enableDaprInstrumentation: enableDaprInstrumentation + containerRegistryUserAssignedIdentityId: supportingServices.outputs.containerRegistryUserAssignedIdentityId + logAnalyticsWorkspaceId: spoke.outputs.logAnalyticsWorkspaceId + } +} + +module sampleApplication 'modules/sample-application/deploy.sample-application.bicep' = if (deploySampleApplication) { + name: take('sampleApplication-${deployment().name}-deployment', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId + workloadProfileName: containerAppsEnvironment.outputs.workloadProfileNames[0] + containerRegistryUserAssignedIdentityId: supportingServices.outputs.containerRegistryUserAssignedIdentityId + } +} + +module applicationGateway 'modules/application-gateway/deploy.app-gateway.bicep' = if (exposeContainerAppsWith == 'applicationGateway') { + name: take('applicationGateway-${deployment().name}-deployment', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + environment: environment + workloadName: workloadName + applicationGatewayCertificateKeyName: applicationGatewayCertificateKeyName + applicationGatewayFqdn: applicationGatewayFqdn + applicationGatewayPrimaryBackendEndFqdn: (deploySampleApplication) + ? sampleApplication.outputs.helloWorldAppFqdn + : '' + applicationGatewaySubnetId: spoke.outputs.spokeApplicationGatewaySubnetId + base64Certificate: base64Certificate + keyVaultId: supportingServices.outputs.keyVaultResourceId + deployZoneRedundantResources: deployZoneRedundantResources + enableDdosProtection: enableDdosProtection + applicationGatewayLogAnalyticsId: spoke.outputs.logAnalyticsWorkspaceId + } +} + +module frontDoor 'modules/front-door/deploy.front-door.bicep' = if (exposeContainerAppsWith == 'frontDoor') { + name: take('frontDoor-${deployment().name}-deployment', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + environment: environment + workloadName: workloadName + containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId + frontDoorOriginHostName: (deploySampleApplication) ? sampleApplication.outputs.helloWorldAppFqdn : '' + privateLinkSubnetId: spoke.outputs.spokeInfraSubnetId + } +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.acalza-hostingenvironment.${substring(uniqueString(deployment().name, location), 0, 4)}' + location: location + 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' + } + } + } + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +// Spoke +@description('The name of the Spoke resource group.') +output spokeResourceGroupName string = spoke.outputs.spokeResourceGroupName + +@description('The resource ID of the Spoke Virtual Network.') +output spokeVNetResourceId string = spoke.outputs.spokeVNetId + +@description('The name of the Spoke Virtual Network.') +output spokeVnetName string = spoke.outputs.spokeVNetName + +@description('The resource ID of the Spoke Infrastructure Subnet.') +output spokeInfraSubnetResourceId string = spoke.outputs.spokeInfraSubnetId + +@description('The name of the Spoke Infrastructure Subnet.') +output spokeInfraSubnetName string = spoke.outputs.spokeInfraSubnetName + +@description('The name of the Spoke Private Endpoints Subnet.') +output spokePrivateEndpointsSubnetName string = spoke.outputs.spokePrivateEndpointsSubnetName + +@description('The resource ID of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty.') +output spokeApplicationGatewaySubnetResourceId string = spoke.outputs.spokeApplicationGatewaySubnetId + +@description('The name of the Spoke Application Gateway Subnet. If "spokeApplicationGatewaySubnetAddressPrefix" is empty, the subnet will not be created and the value returned is empty.') +output spokeApplicationGatewaySubnetName string = spoke.outputs.spokeApplicationGatewaySubnetName + +@description('The resource ID of the Log Analytics workspace created in the spoke vnet.') +output logAnalyticsWorkspaceResourceId string = spoke.outputs.logAnalyticsWorkspaceId + +@description('The name of the jump box virtual machine.') +output vmJumpBoxName string = spoke.outputs.vmJumpBoxName + +// Supporting Services +@description('The resource ID of the container registry.') +output containerRegistryResourceId string = supportingServices.outputs.containerRegistryId + +@description('The name of the container registry.') +output containerRegistryName string = supportingServices.outputs.containerRegistryName + +@description('The name of the container registry login server.') +output containerRegistryLoginServer string = supportingServices.outputs.containerRegistryLoginServer + +@description('The resource ID of the user assigned managed identity for the container registry to be able to pull images from it.') +output containerRegistryUserAssignedIdentityResourceId string = supportingServices.outputs.containerRegistryUserAssignedIdentityId + +@description('The resource ID of the key vault.') +output keyVaultResourceId string = supportingServices.outputs.keyVaultResourceId + +@description('The name of the Azure key vault.') +output keyVaultName string = supportingServices.outputs.keyVaultName + +// Application Gateway +@description('The resource ID of the Azure Application Gateway.') +output applicationGatewayResourceId string = (exposeContainerAppsWith == 'applicationGateway') + ? applicationGateway.outputs.applicationGatewayResourceId + : '' + +@description('The FQDN of the Azure Application Gateway.') +output applicationGatewayFqdn string = (exposeContainerAppsWith == 'applicationGateway') + ? applicationGateway.outputs.applicationGatewayFqdn + : '' + +@description('The public IP address of the Azure Application Gateway.') +output applicationGatewayPublicIp string = (exposeContainerAppsWith == 'applicationGateway') + ? applicationGateway.outputs.applicationGatewayPublicIp + : '' + +// Container Apps Environment +@description('The resource ID of the container apps environment.') +output containerAppsEnvironmentResourceId string = containerAppsEnvironment.outputs.containerAppsEnvironmentId + +@description('The name of the container apps environment.') +output containerAppsEnvironmentName string = containerAppsEnvironment.outputs.containerAppsEnvironmentName + +@description('The name of application Insights instance.') +output applicationInsightsName string = (enableApplicationInsights) + ? containerAppsEnvironment.outputs.applicationInsightsName + : '' diff --git a/avm/ptn/aca-lza/hosting-environment/main.json b/avm/ptn/aca-lza/hosting-environment/main.json new file mode 100644 index 0000000000..dff005463f --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/main.json @@ -0,0 +1,43542 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "16535725536619244187" + }, + "name": "Container Apps Landing Zone Accelerator", + "description": "This Azure Container Apps pattern module represents an Azure Container Apps deployment aligned with the cloud adoption framework", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workloadName": { + "type": "string", + "defaultValue": "aca-lza", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "Optional. The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "hubVirtualNetworkResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the hub virtual network. If set, the spoke virtual network will be peered with the hub virtual network. Default is empty." + } + }, + "bastionResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the bastion host. If set, the spoke virtual network will be peered with the hub virtual network and the bastion host will be allowed to connect to the jump box. Default is empty." + } + }, + "networkApplianceIpAddress": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If set, the spoke virtual network will be peered with the hub virtual network and egres traffic will be routed through the network appliance. Default is empty." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags related to the Azure Container Apps deployment. Default is empty." + } + }, + "environment": { + "type": "string", + "defaultValue": "test", + "maxLength": 8, + "metadata": { + "description": "Optional. The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long. Default is \"test\"." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. The location of the Azure Container Apps deployment. Default is the location of the deployment location." + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "Required. The size of the virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information." + } + }, + "storageAccountType": { + "type": "string", + "defaultValue": "Standard_LRS", + "metadata": { + "description": "Optional. The storage account type to use for the jump box. Defaults to Standard_LRS." + } + }, + "vmAdminUsername": { + "type": "string", + "metadata": { + "description": "Required. The username to use for the virtual machine." + } + }, + "vmAdminPassword": { + "type": "securestring", + "metadata": { + "description": "Required. The password to use for the virtual machine." + } + }, + "vmLinuxSshAuthorizedKey": { + "type": "securestring", + "metadata": { + "description": "Required. The SSH public key to use for the virtual machine." + } + }, + "vmAuthenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Optional. Type of authentication to use on the Virtual Machine. SSH key is recommended. Default is \"password\"." + } + }, + "vmJumpboxOSType": { + "type": "string", + "defaultValue": "none", + "allowedValues": [ + "linux", + "windows", + "none" + ], + "metadata": { + "description": "Optional. The operating system type of the virtual machine. Default is \"none\" which results in no VM deployment. Default is \"none\"." + } + }, + "vmJumpBoxSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Required. CIDR to use for the virtual machine subnet." + } + }, + "spokeResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the resource group to create the resources in. If set, it overrides the name generated by the template. Default is empty." + } + }, + "spokeVNetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. CIDR of the Spoke Virtual Network." + } + }, + "spokeInfraSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Required. CIDR of the Spoke Infrastructure Subnet." + } + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Required. CIDR of the Spoke Private Endpoints Subnet." + } + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "Required. CIDR of the Spoke Application Gateway Subnet." + } + }, + "enableApplicationInsights": { + "type": "bool", + "metadata": { + "description": "Required. Enable or disable the createion of Application Insights." + } + }, + "enableDaprInstrumentation": { + "type": "bool", + "metadata": { + "description": "Required. Enable or disable Dapr Application Instrumentation Key used for Dapr telemetry. If Application Insights is not enabled, this parameter is ignored." + } + }, + "applicationGatewayFqdn": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The FQDN of the Application Gateway. Required and must match if the TLS Certificate is provided. Default is empty." + } + }, + "base64Certificate": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault and assigned to the Application Gateway listener. Default is empty." + } + }, + "applicationGatewayCertificateKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the certificate key to use for Application Gateway certificate." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployZoneRedundantResources": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false. Default is true." + } + }, + "exposeContainerAppsWith": { + "type": "string", + "defaultValue": "applicationGateway", + "allowedValues": [ + "applicationGateway", + "frontDoor" + ], + "metadata": { + "description": "Optional. Specify the way container apps is going to be exposed. Options are applicationGateway or frontDoor. Default is \"applicationGateway\"." + } + }, + "deploySampleApplication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Deploy sample application to the container apps environment. Default is false." + } + }, + "enableDdosProtection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. DDoS protection mode. see https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus. Default is \"false\"." + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "namingRules": "[json(variables('$fxv#0'))]", + "rgSpokeName": "[if(not(empty(parameters('spokeResourceGroupName'))), parameters('spokeResourceGroupName'), format('{0}-{1}-spoke-{2}-{3}', variables('namingRules').resourceTypeAbbreviations.resourceGroup, parameters('workloadName'), parameters('environment'), variables('namingRules').regionAbbreviations[toLower(parameters('location'))]))]" + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.acalza-hostingenvironment.{0}', substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "location": "[parameters('location')]", + "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" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('sharedNamingDeployment-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeResourceGroupName.value)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64))]" + ], + "metadata": { + "description": "User-configured naming rules" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('spoke-{0}-deployment', deployment().name), 64)]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "spokeResourceGroupName": { + "value": "[variables('rgSpokeName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "hubVNetId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "bastionResourceId": { + "value": "[parameters('bastionResourceId')]" + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "value": "[parameters('spokeApplicationGatewaySubnetAddressPrefix')]" + }, + "spokeInfraSubnetAddressPrefix": { + "value": "[parameters('spokeInfraSubnetAddressPrefix')]" + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "value": "[parameters('spokePrivateEndpointsSubnetAddressPrefix')]" + }, + "spokeVNetAddressPrefixes": { + "value": "[parameters('spokeVNetAddressPrefixes')]" + }, + "networkApplianceIpAddress": { + "value": "[parameters('networkApplianceIpAddress')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "vmZone": "[if(parameters('deployZoneRedundantResources'), createObject('value', 2), createObject('value', 0))]", + "storageAccountType": { + "value": "[parameters('storageAccountType')]" + }, + "vmAdminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "vmAdminPassword": { + "value": "[parameters('vmAdminPassword')]" + }, + "vmLinuxSshAuthorizedKey": { + "value": "[parameters('vmLinuxSshAuthorizedKey')]" + }, + "vmJumpboxOSType": { + "value": "[parameters('vmJumpboxOSType')]" + }, + "vmJumpBoxSubnetAddressPrefix": { + "value": "[parameters('vmJumpBoxSubnetAddressPrefix')]" + }, + "vmAuthenticationType": { + "value": "[parameters('vmAuthenticationType')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "16961623286769564475" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "The location where the resources will be created. This should be the same region as the hub." + } + }, + "spokeResourceGroupName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the resource group to create the resources in. If set, it overrides the name generated by the template." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "hubVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing hub virtual network." + } + }, + "bastionResourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the bastion host." + } + }, + "spokeVNetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "CIDR of the spoke virtual network. For most landing zone implementations, the spoke network would have been created by your platform team." + } + }, + "spokeInfraSubnetName": { + "type": "string", + "defaultValue": "snet-infra", + "metadata": { + "description": "Optional. The name of the subnet to create for the spoke infrastructure. If set, it overrides the name generated by the template." + } + }, + "spokeInfraSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "CIDR of the spoke infrastructure subnet." + } + }, + "spokePrivateEndpointsSubnetName": { + "type": "string", + "defaultValue": "snet-pep", + "metadata": { + "description": "Optional. The name of the subnet to create for the spoke private endpoints. If set, it overrides the name generated by the template." + } + }, + "spokePrivateEndpointsSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "CIDR of the spoke private endpoints subnet." + } + }, + "spokeApplicationGatewaySubnetName": { + "type": "string", + "defaultValue": "snet-agw", + "metadata": { + "description": "Optional. The name of the subnet to create for the spoke application gateway. If set, it overrides the name generated by the template." + } + }, + "spokeApplicationGatewaySubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "CIDR of the spoke Application Gateway subnet. If the value is empty, this subnet will not be created." + } + }, + "networkApplianceIpAddress": { + "type": "string", + "metadata": { + "description": "The IP address of the network appliance (e.g. firewall) that will be used to route traffic to the internet." + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "The size of the jump box virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information." + } + }, + "vmZone": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The zone to create the jump box in. Defaults to 0." + } + }, + "storageAccountType": { + "type": "string", + "defaultValue": "Standard_LRS", + "metadata": { + "description": "Optional. The storage account type to use for the jump box. Defaults to Standard_LRS." + } + }, + "vmAdminUsername": { + "type": "string", + "metadata": { + "description": "The username to use for the jump box." + } + }, + "vmAdminPassword": { + "type": "securestring", + "metadata": { + "description": "The password to use for the jump box." + } + }, + "vmLinuxSshAuthorizedKey": { + "type": "securestring", + "metadata": { + "description": "The SSH public key to use for the jump box. Only relevant for Linux." + } + }, + "vmJumpboxOSType": { + "type": "string", + "defaultValue": "none", + "allowedValues": [ + "linux", + "windows", + "none" + ], + "metadata": { + "description": "The OS of the jump box virtual machine to create. If set to \"none\", no jump box will be created." + } + }, + "vmSubnetName": { + "type": "string", + "defaultValue": "snet-jumpbox", + "metadata": { + "description": "Optional. The name of the subnet to create for the jump box. If set, it overrides the name generated by the template." + } + }, + "vmJumpBoxSubnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "CIDR to use for the jump box subnet." + } + }, + "vmAuthenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Type of authentication to use on the Virtual Machine. SSH key is recommended." + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "locationVar": "[if(equals(parameters('location'), 'francecentral'), 'centralfrance', parameters('location'))]", + "namingRules": "[json(variables('$fxv#0'))]", + "rgSpokeName": "[if(not(empty(parameters('spokeResourceGroupName'))), parameters('spokeResourceGroupName'), format('{0}-{1}-{2}-{3}-spoke', variables('namingRules').resourceTypeAbbreviations.resourceGroup, parameters('workloadName'), parameters('environment'), variables('namingRules').regionAbbreviations[toLower(parameters('location'))]))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('rg-{0}', deployment().name), 64)]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('rgSpokeName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6727498516867168543" + }, + "name": "Resource Groups", + "description": "This module deploys a Resource Group.", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Resource Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location of the Resource Group. It uses the deployment's location when not provided." + } + }, + "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 of the storage account resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.resources-resourcegroup.{0}.{1}', replace('0.2.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "location": "[parameters('location')]", + "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" + } + } + } + } + }, + "resourceGroup": { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2021-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": {} + }, + "resourceGroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RG-Lock', uniqueString(deployment().name, parameters('location')))]", + "resourceGroup": "[parameters('name')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "lock": { + "value": "[parameters('lock')]" + }, + "name": { + "value": "[parameters('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.25.53.49325", + "templateHash": "7668050384482764751" + } + }, + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Resource Group." + } + } + }, + "resources": { + "resourceGroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "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": [ + "resourceGroup" + ] + }, + "resourceGroup_roleAssignments": { + "condition": "[not(empty(coalesce(parameters('roleAssignments'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RG-RoleAssignments', uniqueString(deployment().name, parameters('location')))]", + "resourceGroup": "[parameters('name')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "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.25.53.49325", + "templateHash": "3156241559834375483" + } + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Template Spec Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c9b6475-caf0-4164-b5a1-2142a7116f4b')]", + "Template Spec Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '392ae280-861d-42bd-9ea5-08ee6d83b80e')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "resourceGroup_roleAssignments": { + "copy": { + "name": "resourceGroup_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(resourceGroup().id, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + } + } + } + } + }, + "dependsOn": [ + "resourceGroup" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the resource group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the resource group." + }, + "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('resourceGroup', '2021-04-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('spokeNamingDeployment-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('rg-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('rg-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "User-configured naming rules" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('vnetSpoke-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vnetSpoke]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "addressPrefixes": { + "value": "[parameters('spokeVNetAddressPrefixes')]" + }, + "subnets": "[if(not(equals(parameters('vmJumpboxOSType'), 'none')), createObject('value', concat(if(not(empty(parameters('spokeApplicationGatewaySubnetAddressPrefix'))), concat(createArray(createObject('name', parameters('spokeInfraSubnetName'), 'addressPrefix', parameters('spokeInfraSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, 'routeTableResourceId', if(and(not(empty(parameters('hubVNetId'))), not(empty(parameters('networkApplianceIpAddress')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('egressLockdownUdr-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, null()), 'delegations', createArray(createObject('name', 'envdelegation', 'properties', createObject('serviceName', 'Microsoft.App/environments')))), createObject('name', parameters('spokePrivateEndpointsSubnetName'), 'addressPrefix', parameters('spokePrivateEndpointsSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgPep-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value)), createArray(createObject('name', parameters('spokeApplicationGatewaySubnetName'), 'addressPrefix', parameters('spokeApplicationGatewaySubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgAppGw-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value))), createArray(createObject('name', parameters('spokeInfraSubnetName'), 'addressPrefix', parameters('spokeInfraSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, 'routeTableResourceId', if(and(not(empty(parameters('hubVNetId'))), not(empty(parameters('networkApplianceIpAddress')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('egressLockdownUdr-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, null()), 'delegations', createArray(createObject('name', 'envdelegation', 'properties', createObject('serviceName', 'Microsoft.App/environments')))), createObject('name', parameters('spokePrivateEndpointsSubnetName'), 'addressPrefix', parameters('spokePrivateEndpointsSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgPep-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value))), createArray(createObject('name', parameters('vmSubnetName'), 'addressPrefix', parameters('vmJumpBoxSubnetAddressPrefix'))))), if(not(empty(parameters('spokeApplicationGatewaySubnetAddressPrefix'))), createObject('value', concat(createArray(createObject('name', parameters('spokeInfraSubnetName'), 'addressPrefix', parameters('spokeInfraSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, 'routeTableResourceId', if(and(not(empty(parameters('hubVNetId'))), not(empty(parameters('networkApplianceIpAddress')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('egressLockdownUdr-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, null()), 'delegations', createArray(createObject('name', 'envdelegation', 'properties', createObject('serviceName', 'Microsoft.App/environments')))), createObject('name', parameters('spokePrivateEndpointsSubnetName'), 'addressPrefix', parameters('spokePrivateEndpointsSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgPep-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value)), createArray(createObject('name', parameters('spokeApplicationGatewaySubnetName'), 'addressPrefix', parameters('spokeApplicationGatewaySubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgAppGw-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value)))), createObject('value', createArray(createObject('name', parameters('spokeInfraSubnetName'), 'addressPrefix', parameters('spokeInfraSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, 'routeTableResourceId', if(and(not(empty(parameters('hubVNetId'))), not(empty(parameters('networkApplianceIpAddress')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('egressLockdownUdr-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value, null()), 'delegations', createArray(createObject('name', 'envdelegation', 'properties', createObject('serviceName', 'Microsoft.App/environments')))), createObject('name', parameters('spokePrivateEndpointsSubnetName'), 'addressPrefix', parameters('spokePrivateEndpointsSubnetAddressPrefix'), 'networkSecurityGroupResourceId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgPep-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value)))))]", + "peerings": "[if(not(empty(parameters('hubVNetId'))), createObject('value', createArray(createObject('allowForwardedTraffic', true(), 'allowGatewayTransit', false(), 'allowVirtualNetworkAccess', true(), 'remotePeeringAllowForwardedTraffic', true(), 'remotePeeringAllowVirtualNetworkAccess', true(), 'remotePeeringEnabled', true(), 'remotePeeringName', 'spokeToHub', 'remoteVirtualNetworkId', parameters('hubVNetId'), 'useRemoteGateways', false()))), 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.26.170.59819", + "templateHash": "18408205474040416108" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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 for the Virtual Network." + } + }, + "subnets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Virtual Network Peerings 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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.1.6', '.', '-'), 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": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "subnets", + "count": "[length(parameters('subnets'))]", + "input": { + "name": "[parameters('subnets')[copyIndex('subnets')].name]", + "properties": { + "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", + "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", + "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", + "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", + "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" + } + } + } + ], + "addressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + }, + "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())]" + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(parameters('subnets'))]", + "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": "[parameters('subnets')[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[parameters('subnets')[copyIndex()].addressPrefix]" + }, + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", + "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", + "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), 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.26.170.59819", + "templateHash": "17306638026226376877" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Optional. 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", + "metadata": { + "description": "Required. The address prefix for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The delegations to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of address prefixes for the subnet." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "ipAllocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of IpAllocation which reference this subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-04-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "addressPrefix": "[parameters('addressPrefix')]", + "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())]", + "serviceEndpoints": "[parameters('serviceEndpoints')]", + "delegations": "[parameters('delegations')]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "ipAllocations": "[parameters('ipAllocations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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'))]" + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[reference('subnet').addressPrefix]" + }, + "subnetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_local": { + "copy": { + "name": "virtualNetwork_peering_local", + "count": "[length(parameters('peerings'))]" + }, + "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')]" + }, + "remoteVirtualNetworkId": { + "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17624189975510507274" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "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." + } + }, + "remoteVirtualNetworkId": { + "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": "2023-04-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('remoteVirtualNetworkId')]" + } + } + } + ], + "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_peering_remote": { + "copy": { + "name": "virtualNetwork_peering_remote", + "count": "[length(parameters('peerings'))]" + }, + "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", + "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + }, + "remoteVirtualNetworkId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17624189975510507274" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "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." + } + }, + "remoteVirtualNetworkId": { + "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": "2023-04-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('remoteVirtualNetworkId')]" + } + } + } + ], + "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" + ] + } + }, + "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(parameters('subnets'))]", + "input": "[parameters('subnets')[copyIndex()].name]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('egressLockdownUdr-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgAppGw-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('nsgPep-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "The spoke virtual network in which the workload will run from. This virtual network would normally already be provisioned by your subscription vending process, and only the subnets would need to be configured." + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('logAnalyticsWs-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.logAnalyticsWorkspace]" + }, + "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.25.53.49325", + "templateHash": "6354173151975740736" + }, + "name": "Log Analytics Workspaces", + "description": "This module deploys a Log Analytics Workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. List of storage accounts to be read by the workspace." + } + }, + "linkedServices": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of services to be linked." + } + }, + "linkedStorageAccounts": { + "type": "array", + "defaultValue": [], + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Kusto Query Language searches to save." + } + }, + "dataExports": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW data export instances to be deployed." + } + }, + "dataSources": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW data sources to configure." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW custom tables to be deployed." + } + }, + "gallerySolutions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of gallerySolutions to be created in the log analytics workspace." + } + }, + "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/managedIdentitiesType", + "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." + } + }, + "useResourcePermissions": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "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", + "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 of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "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()), '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 (Preview)": "[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": "2023-07-01", + "name": "[format('46d3xbcp.res.operationalinsights-workspace.{0}.{1}', replace('0.3.4', '.', '-'), 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": "2022-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "features": { + "searchVersion": 1, + "enableLogAccessUsingOnlyResourcePermissions": "[parameters('useResourcePermissions')]" + }, + "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')]" + }, + "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": "[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_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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_storageInsightConfigs": { + "copy": { + "name": "logAnalyticsWorkspace_storageInsightConfigs", + "count": "[length(parameters('storageInsightsConfigs'))]" + }, + "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": "[if(contains(parameters('storageInsightsConfigs')[copyIndex()], 'containers'), createObject('value', parameters('storageInsightsConfigs')[copyIndex()].containers), createObject('value', createArray()))]", + "tables": "[if(contains(parameters('storageInsightsConfigs')[copyIndex()], 'tables'), createObject('value', parameters('storageInsightsConfigs')[copyIndex()].tables), createObject('value', createArray()))]", + "storageAccountResourceId": { + "value": "[parameters('storageInsightsConfigs')[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.25.53.49325", + "templateHash": "4862843187650272248" + }, + "name": "Log Analytics Workspace Storage Insight Configs", + "description": "This module deploys a Log Analytics Workspace Storage Insight Config.", + "owner": "Azure/module-maintainers" + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The names of the blob containers that the workspace should read." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The names of the Azure tables that the workspace should read." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "storageinsightconfig": { + "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs", + "apiVersion": "2020-08-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(resourceId('Microsoft.Storage/storageAccounts', last(split(parameters('storageAccountResourceId'), '/'))), '2022-09-01').keys[0].value]" + } + }, + "dependsOn": [ + "storageAccount", + "workspace" + ] + } + }, + "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(parameters('linkedServices'))]" + }, + "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": "[parameters('linkedServices')[copyIndex()].name]" + }, + "resourceId": "[if(contains(parameters('linkedServices')[copyIndex()], 'resourceId'), createObject('value', parameters('linkedServices')[copyIndex()].resourceId), createObject('value', ''))]", + "writeAccessResourceId": "[if(contains(parameters('linkedServices')[copyIndex()], 'writeAccessResourceId'), createObject('value', parameters('linkedServices')[copyIndex()].writeAccessResourceId), 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.25.53.49325", + "templateHash": "14301767156435143002" + }, + "name": "Log Analytics Workspace Linked Services", + "description": "This module deploys a Log Analytics Workspace Linked Service.", + "owner": "Azure/module-maintainers" + }, + "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", + "defaultValue": "", + "metadata": { + "description": "Required. 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", + "defaultValue": "", + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "linkedService": { + "type": "Microsoft.OperationalInsights/workspaces/linkedServices", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resourceId": "[parameters('resourceId')]", + "writeAccessResourceId": "[if(empty(parameters('writeAccessResourceId')), null(), parameters('writeAccessResourceId'))]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "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(parameters('linkedStorageAccounts'))]" + }, + "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": "[parameters('linkedStorageAccounts')[copyIndex()].name]" + }, + "resourceId": { + "value": "[parameters('linkedStorageAccounts')[copyIndex()].resourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "6713282874166856483" + }, + "name": "Log Analytics Workspace Linked Storage Accounts", + "description": "This module deploys a Log Analytics Workspace Linked Storage Account.", + "owner": "Azure/module-maintainers" + }, + "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." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access." + } + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "properties": { + "storageAccountIds": [ + "[parameters('resourceId')]" + ] + } + } + ], + "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(parameters('savedSearches'))]" + }, + "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}', parameters('savedSearches')[copyIndex()].name, uniqueString(deployment().name))]" + }, + "etag": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'etag')]" + }, + "displayName": { + "value": "[parameters('savedSearches')[copyIndex()].displayName]" + }, + "category": { + "value": "[parameters('savedSearches')[copyIndex()].category]" + }, + "query": { + "value": "[parameters('savedSearches')[copyIndex()].query]" + }, + "functionAlias": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'functionAlias')]" + }, + "functionParameters": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'functionParameters')]" + }, + "version": { + "value": "[tryGet(parameters('savedSearches')[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.25.53.49325", + "templateHash": "17950009471823327560" + }, + "name": "Log Analytics Workspace Saved Searches", + "description": "This module deploys a Log Analytics Workspace Saved Search.", + "owner": "Azure/module-maintainers" + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + }, + "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": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "savedSearch": { + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2020-08-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')]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "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(parameters('dataExports'))]" + }, + "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": "[parameters('dataExports')[copyIndex()].name]" + }, + "destination": "[if(contains(parameters('dataExports')[copyIndex()], 'destination'), createObject('value', parameters('dataExports')[copyIndex()].destination), createObject('value', createObject()))]", + "enable": "[if(contains(parameters('dataExports')[copyIndex()], 'enable'), createObject('value', parameters('dataExports')[copyIndex()].enable), createObject('value', false()))]", + "tableNames": "[if(contains(parameters('dataExports')[copyIndex()], 'tableNames'), createObject('value', parameters('dataExports')[copyIndex()].tableNames), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "12543023571728523937" + }, + "name": "Log Analytics Workspace Data Exports", + "description": "This module deploys a Log Analytics Workspace Data Export.", + "owner": "Azure/module-maintainers" + }, + "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": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Destination properties." + } + }, + "enable": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Active when enabled." + } + }, + "tableNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of tables to export, for example: ['Heartbeat', 'SecurityEvent']." + } + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/dataExports", + "apiVersion": "2020-08-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(parameters('dataSources'))]" + }, + "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": "[parameters('dataSources')[copyIndex()].name]" + }, + "kind": { + "value": "[parameters('dataSources')[copyIndex()].kind]" + }, + "linkedResourceId": "[if(contains(parameters('dataSources')[copyIndex()], 'linkedResourceId'), createObject('value', parameters('dataSources')[copyIndex()].linkedResourceId), createObject('value', ''))]", + "eventLogName": "[if(contains(parameters('dataSources')[copyIndex()], 'eventLogName'), createObject('value', parameters('dataSources')[copyIndex()].eventLogName), createObject('value', ''))]", + "eventTypes": "[if(contains(parameters('dataSources')[copyIndex()], 'eventTypes'), createObject('value', parameters('dataSources')[copyIndex()].eventTypes), createObject('value', createArray()))]", + "objectName": "[if(contains(parameters('dataSources')[copyIndex()], 'objectName'), createObject('value', parameters('dataSources')[copyIndex()].objectName), createObject('value', ''))]", + "instanceName": "[if(contains(parameters('dataSources')[copyIndex()], 'instanceName'), createObject('value', parameters('dataSources')[copyIndex()].instanceName), createObject('value', ''))]", + "intervalSeconds": "[if(contains(parameters('dataSources')[copyIndex()], 'intervalSeconds'), createObject('value', parameters('dataSources')[copyIndex()].intervalSeconds), createObject('value', 60))]", + "counterName": "[if(contains(parameters('dataSources')[copyIndex()], 'counterName'), createObject('value', parameters('dataSources')[copyIndex()].counterName), createObject('value', ''))]", + "state": "[if(contains(parameters('dataSources')[copyIndex()], 'state'), createObject('value', parameters('dataSources')[copyIndex()].state), createObject('value', ''))]", + "syslogName": "[if(contains(parameters('dataSources')[copyIndex()], 'syslogName'), createObject('value', parameters('dataSources')[copyIndex()].syslogName), createObject('value', ''))]", + "syslogSeverities": "[if(contains(parameters('dataSources')[copyIndex()], 'syslogSeverities'), createObject('value', parameters('dataSources')[copyIndex()].syslogSeverities), createObject('value', createArray()))]", + "performanceCounters": "[if(contains(parameters('dataSources')[copyIndex()], 'performanceCounters'), createObject('value', parameters('dataSources')[copyIndex()].performanceCounters), 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.25.53.49325", + "templateHash": "14032975851567807564" + }, + "name": "Log Analytics Workspace Datasources", + "description": "This module deploys a Log Analytics Workspace Data Source.", + "owner": "Azure/module-maintainers" + }, + "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 solution." + } + }, + "kind": { + "type": "string", + "defaultValue": "AzureActivityLog", + "allowedValues": [ + "AzureActivityLog", + "WindowsEvent", + "WindowsPerformanceCounter", + "IISLogs", + "LinuxSyslog", + "LinuxSyslogCollection", + "LinuxPerformanceObject", + "LinuxPerformanceCollection" + ], + "metadata": { + "description": "Required. The kind of the DataSource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + }, + "linkedResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the resource to be linked." + } + }, + "eventLogName": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "", + "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", + "defaultValue": "", + "metadata": { + "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter." + } + }, + "state": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection." + } + }, + "syslogName": { + "type": "string", + "defaultValue": "", + "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": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "dataSource": { + "type": "Microsoft.OperationalInsights/workspaces/dataSources", + "apiVersion": "2020-08-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())]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "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(parameters('tables'))]" + }, + "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": "[parameters('tables')[copyIndex()].name]" + }, + "plan": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'plan')]" + }, + "schema": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'schema')]" + }, + "retentionInDays": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'retentionInDays')]" + }, + "totalRetentionInDays": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'totalRetentionInDays')]" + }, + "restoredLogs": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'restoredLogs')]" + }, + "searchResults": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'searchResults')]" + }, + "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.25.53.49325", + "templateHash": "4932423807790181892" + }, + "name": "Log Analytics Workspace Tables", + "description": "This module deploys a Log Analytics Workspace Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "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": { + "type": "object", + "defaultValue": {}, + "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": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table's schema." + } + }, + "searchResults": { + "type": "object", + "defaultValue": {}, + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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": "2022-10-01", + "name": "[parameters('workspaceName')]" + }, + "table": { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2022-10-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')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}/tables/{1}', parameters('workspaceName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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(parameters('gallerySolutions'))]" + }, + "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": "[parameters('gallerySolutions')[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "product": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'product'), createObject('value', parameters('gallerySolutions')[copyIndex()].product), createObject('value', 'OMSGallery'))]", + "publisher": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'publisher'), createObject('value', parameters('gallerySolutions')[copyIndex()].publisher), createObject('value', 'Microsoft'))]", + "enableTelemetry": { + "value": "[coalesce(tryGet(parameters('gallerySolutions')[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "18444780972506374592" + }, + "name": "Operations Management Solutions", + "description": "This module deploys an Operations Management Solution.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`." + } + }, + "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." + } + }, + "product": { + "type": "string", + "defaultValue": "OMSGallery", + "metadata": { + "description": "Optional. The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive." + } + }, + "publisher": { + "type": "string", + "defaultValue": "Microsoft", + "metadata": { + "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "solutionName": "[if(equals(parameters('publisher'), 'Microsoft'), format('{0}({1})', parameters('name'), parameters('logAnalyticsWorkspaceName')), parameters('name'))]", + "solutionProduct": "[if(equals(parameters('publisher'), 'Microsoft'), format('OMSGallery/{0}', parameters('name')), parameters('product'))]" + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.operationsmanagement-solution.{0}.{1}', replace('0.1.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" + } + } + } + } + }, + { + "type": "Microsoft.OperationsManagement/solutions", + "apiVersion": "2015-11-01-preview", + "name": "[variables('solutionName')]", + "location": "[parameters('location')]", + "properties": { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + }, + "plan": { + "name": "[variables('solutionName')]", + "promotionCode": "", + "product": "[variables('solutionProduct')]", + "publisher": "[parameters('publisher')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed solution." + }, + "value": "[variables('solutionName')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed solution." + }, + "value": "[resourceId('Microsoft.OperationsManagement/solutions', variables('solutionName'))]" + }, + "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(resourceId('Microsoft.OperationsManagement/solutions', variables('solutionName')), '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', '2022-10-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('logAnalyticsWorkspace', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "The log sink for Azure Diagnostics" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('nsgContainerAppsEnvironment-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.containerAppsEnvironmentNsg]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "securityRules": { + "value": [ + { + "name": "Allow_Internal_AKS_Connection_Between_Nodes_And_Control_Plane_UDP", + "properties": { + "description": "internal AKS secure connection between underlying nodes and control plane..", + "protocol": "Udp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "[format('AzureCloud.{0}', variables('locationVar'))]", + "destinationPortRange": "1194", + "access": "Allow", + "priority": 100, + "direction": "Outbound" + } + }, + { + "name": "Allow_Internal_AKS_Connection_Between_Nodes_And_Control_Plane_TCP", + "properties": { + "description": "internal AKS secure connection between underlying nodes and control plane..", + "protocol": "Tcp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "[format('AzureCloud.{0}', variables('locationVar'))]", + "destinationPortRange": "9000", + "access": "Allow", + "priority": 110, + "direction": "Outbound" + } + }, + { + "name": "Allow_Azure_Monitor", + "properties": { + "description": "Allows outbound calls to Azure Monitor.", + "protocol": "Tcp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "[format('AzureCloud.{0}', variables('locationVar'))]", + "destinationPortRange": "443", + "access": "Allow", + "priority": 120, + "direction": "Outbound" + } + }, + { + "name": "Allow_Outbound_443", + "properties": { + "description": "Allowing all outbound on port 443 provides a way to allow all FQDN based outbound dependencies that don't have a static IP", + "protocol": "Tcp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "443", + "access": "Allow", + "priority": 130, + "direction": "Outbound" + } + }, + { + "name": "Allow_NTP_Server", + "properties": { + "description": "NTP server", + "protocol": "Udp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "123", + "access": "Allow", + "priority": 140, + "direction": "Outbound" + } + }, + { + "name": "Allow_Container_Apps_control_plane", + "properties": { + "description": "Container Apps control plane", + "protocol": "Tcp", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRanges": [ + "5671", + "5672" + ], + "access": "Allow", + "priority": 150, + "direction": "Outbound" + } + }, + { + "name": "deny-hop-outbound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRanges": [ + "3389", + "22" + ], + "access": "Deny", + "priority": 200, + "direction": "Outbound", + "sourceAddressPrefix": "VirtualNetwork", + "destinationAddressPrefix": "*" + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "name": "logAnalyticsSettings", + "workspaceResourceId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "8543584719514494080" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(parameters('securityRules'))]", + "input": { + "name": "[parameters('securityRules')[copyIndex('securityRules')].name]", + "properties": { + "protocol": "[parameters('securityRules')[copyIndex('securityRules')].properties.protocol]", + "access": "[parameters('securityRules')[copyIndex('securityRules')].properties.access]", + "priority": "[parameters('securityRules')[copyIndex('securityRules')].properties.priority]", + "direction": "[parameters('securityRules')[copyIndex('securityRules')].properties.direction]", + "description": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'description'), parameters('securityRules')[copyIndex('securityRules')].properties.description, '')]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRange, '')]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRanges, createArray())]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRange, '')]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRanges, createArray())]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefix, '')]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefix, '')]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefixes, createArray())]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefixes, createArray())]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceApplicationSecurityGroups, createArray())]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationApplicationSecurityGroups, 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_securityRules": { + "copy": { + "name": "networkSecurityGroup_securityRules", + "count": "[length(parameters('securityRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkSecurityGroup-SecurityRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityRules')[copyIndex()].name]" + }, + "networkSecurityGroupName": { + "value": "[parameters('name')]" + }, + "protocol": { + "value": "[parameters('securityRules')[copyIndex()].properties.protocol]" + }, + "access": { + "value": "[parameters('securityRules')[copyIndex()].properties.access]" + }, + "priority": { + "value": "[parameters('securityRules')[copyIndex()].properties.priority]" + }, + "direction": { + "value": "[parameters('securityRules')[copyIndex()].properties.direction]" + }, + "description": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'description'), createObject('value', parameters('securityRules')[copyIndex()].properties.description), createObject('value', ''))]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRange), createObject('value', ''))]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRanges), createObject('value', createArray()))]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRange), createObject('value', ''))]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRanges), createObject('value', createArray()))]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefix), createObject('value', ''))]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefix), createObject('value', ''))]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefixes), createObject('value', createArray()))]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefixes), createObject('value', createArray()))]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceApplicationSecurityGroups), createObject('value', createArray()))]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationApplicationSecurityGroups), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "17252857850318600622" + }, + "name": "Network Security Group (NSG) Security Rules", + "description": "This module deploys a Network Security Group (NSG) Security Rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "networkSecurityGroupName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment." + } + }, + "access": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "maxLength": 140, + "metadata": { + "description": "Optional. A description for this rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "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", + "metadata": { + "description": "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", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('networkSecurityGroupName'), parameters('name'))]", + "properties": { + "access": "[parameters('access')]", + "description": "[parameters('description')]", + "destinationAddressPrefix": "[parameters('destinationAddressPrefix')]", + "destinationAddressPrefixes": "[parameters('destinationAddressPrefixes')]", + "destinationApplicationSecurityGroups": "[parameters('destinationApplicationSecurityGroups')]", + "destinationPortRange": "[parameters('destinationPortRange')]", + "destinationPortRanges": "[parameters('destinationPortRanges')]", + "direction": "[parameters('direction')]", + "priority": "[parameters('priority')]", + "protocol": "[parameters('protocol')]", + "sourceAddressPrefix": "[parameters('sourceAddressPrefix')]", + "sourceAddressPrefixes": "[parameters('sourceAddressPrefixes')]", + "sourceApplicationSecurityGroups": "[parameters('sourceApplicationSecurityGroups')]", + "sourcePortRange": "[parameters('sourcePortRange')]", + "sourcePortRanges": "[parameters('sourcePortRanges')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the security rule was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the security rule." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups/securityRules', parameters('networkSecurityGroupName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the security rule." + }, + "value": "[parameters('name')]" + } + } + } + }, + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "Network security group rules for the Container Apps cluster." + } + }, + { + "condition": "[not(empty(parameters('spokeApplicationGatewaySubnetAddressPrefix')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('nsgAppGw-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGatewayNsg]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "securityRules": { + "value": [ + { + "name": "HealthProbes", + "properties": { + "description": "allow HealthProbes from gateway Manager.", + "protocol": "*", + "sourceAddressPrefix": "GatewayManager", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "65200-65535", + "access": "Allow", + "priority": 100, + "direction": "Inbound" + } + }, + { + "name": "Allow_TLS", + "properties": { + "description": "allow https incoming connections", + "protocol": "*", + "sourceAddressPrefix": "*", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "443", + "access": "Allow", + "priority": 110, + "direction": "Inbound" + } + }, + { + "name": "Allow_HTTP", + "properties": { + "description": "allow http incoming connections", + "protocol": "*", + "sourceAddressPrefix": "*", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "80", + "access": "Allow", + "priority": 120, + "direction": "Inbound" + } + }, + { + "name": "Allow_AzureLoadBalancer", + "properties": { + "description": "allow AzureLoadBalancer incoming connections", + "protocol": "*", + "sourceAddressPrefix": "AzureLoadBalancer", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRange": "80", + "access": "Allow", + "priority": 130, + "direction": "Inbound" + } + }, + { + "name": "allow-all-outbound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "*", + "access": "Allow", + "priority": 210, + "direction": "Outbound", + "sourceAddressPrefix": "VirtualNetwork", + "destinationAddressPrefix": "*" + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "name": "logAnalyticsSettings", + "workspaceResourceId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "8543584719514494080" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(parameters('securityRules'))]", + "input": { + "name": "[parameters('securityRules')[copyIndex('securityRules')].name]", + "properties": { + "protocol": "[parameters('securityRules')[copyIndex('securityRules')].properties.protocol]", + "access": "[parameters('securityRules')[copyIndex('securityRules')].properties.access]", + "priority": "[parameters('securityRules')[copyIndex('securityRules')].properties.priority]", + "direction": "[parameters('securityRules')[copyIndex('securityRules')].properties.direction]", + "description": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'description'), parameters('securityRules')[copyIndex('securityRules')].properties.description, '')]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRange, '')]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRanges, createArray())]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRange, '')]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRanges, createArray())]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefix, '')]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefix, '')]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefixes, createArray())]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefixes, createArray())]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceApplicationSecurityGroups, createArray())]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationApplicationSecurityGroups, 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_securityRules": { + "copy": { + "name": "networkSecurityGroup_securityRules", + "count": "[length(parameters('securityRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkSecurityGroup-SecurityRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityRules')[copyIndex()].name]" + }, + "networkSecurityGroupName": { + "value": "[parameters('name')]" + }, + "protocol": { + "value": "[parameters('securityRules')[copyIndex()].properties.protocol]" + }, + "access": { + "value": "[parameters('securityRules')[copyIndex()].properties.access]" + }, + "priority": { + "value": "[parameters('securityRules')[copyIndex()].properties.priority]" + }, + "direction": { + "value": "[parameters('securityRules')[copyIndex()].properties.direction]" + }, + "description": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'description'), createObject('value', parameters('securityRules')[copyIndex()].properties.description), createObject('value', ''))]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRange), createObject('value', ''))]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRanges), createObject('value', createArray()))]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRange), createObject('value', ''))]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRanges), createObject('value', createArray()))]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefix), createObject('value', ''))]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefix), createObject('value', ''))]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefixes), createObject('value', createArray()))]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefixes), createObject('value', createArray()))]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceApplicationSecurityGroups), createObject('value', createArray()))]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationApplicationSecurityGroups), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "17252857850318600622" + }, + "name": "Network Security Group (NSG) Security Rules", + "description": "This module deploys a Network Security Group (NSG) Security Rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "networkSecurityGroupName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment." + } + }, + "access": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "maxLength": 140, + "metadata": { + "description": "Optional. A description for this rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "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", + "metadata": { + "description": "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", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('networkSecurityGroupName'), parameters('name'))]", + "properties": { + "access": "[parameters('access')]", + "description": "[parameters('description')]", + "destinationAddressPrefix": "[parameters('destinationAddressPrefix')]", + "destinationAddressPrefixes": "[parameters('destinationAddressPrefixes')]", + "destinationApplicationSecurityGroups": "[parameters('destinationApplicationSecurityGroups')]", + "destinationPortRange": "[parameters('destinationPortRange')]", + "destinationPortRanges": "[parameters('destinationPortRanges')]", + "direction": "[parameters('direction')]", + "priority": "[parameters('priority')]", + "protocol": "[parameters('protocol')]", + "sourceAddressPrefix": "[parameters('sourceAddressPrefix')]", + "sourceAddressPrefixes": "[parameters('sourceAddressPrefixes')]", + "sourceApplicationSecurityGroups": "[parameters('sourceApplicationSecurityGroups')]", + "sourcePortRange": "[parameters('sourcePortRange')]", + "sourcePortRanges": "[parameters('sourcePortRanges')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the security rule was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the security rule." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups/securityRules', parameters('networkSecurityGroupName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the security rule." + }, + "value": "[parameters('name')]" + } + } + } + }, + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "NSG Rules for the Application Gateway." + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('nsgPep-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.pepNsg]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "securityRules": { + "value": [ + { + "name": "deny-hop-outbound", + "properties": { + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRanges": [ + "3389", + "22" + ], + "access": "Deny", + "priority": 200, + "direction": "Outbound", + "sourceAddressPrefix": "VirtualNetwork", + "destinationAddressPrefix": "*" + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "name": "logAnalyticsSettings", + "workspaceResourceId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "8543584719514494080" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(parameters('securityRules'))]", + "input": { + "name": "[parameters('securityRules')[copyIndex('securityRules')].name]", + "properties": { + "protocol": "[parameters('securityRules')[copyIndex('securityRules')].properties.protocol]", + "access": "[parameters('securityRules')[copyIndex('securityRules')].properties.access]", + "priority": "[parameters('securityRules')[copyIndex('securityRules')].properties.priority]", + "direction": "[parameters('securityRules')[copyIndex('securityRules')].properties.direction]", + "description": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'description'), parameters('securityRules')[copyIndex('securityRules')].properties.description, '')]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRange, '')]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRanges, createArray())]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRange, '')]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRanges, createArray())]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefix, '')]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefix, '')]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefixes, createArray())]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefixes, createArray())]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceApplicationSecurityGroups, createArray())]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationApplicationSecurityGroups, 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_securityRules": { + "copy": { + "name": "networkSecurityGroup_securityRules", + "count": "[length(parameters('securityRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkSecurityGroup-SecurityRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityRules')[copyIndex()].name]" + }, + "networkSecurityGroupName": { + "value": "[parameters('name')]" + }, + "protocol": { + "value": "[parameters('securityRules')[copyIndex()].properties.protocol]" + }, + "access": { + "value": "[parameters('securityRules')[copyIndex()].properties.access]" + }, + "priority": { + "value": "[parameters('securityRules')[copyIndex()].properties.priority]" + }, + "direction": { + "value": "[parameters('securityRules')[copyIndex()].properties.direction]" + }, + "description": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'description'), createObject('value', parameters('securityRules')[copyIndex()].properties.description), createObject('value', ''))]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRange), createObject('value', ''))]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRanges), createObject('value', createArray()))]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRange), createObject('value', ''))]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRanges), createObject('value', createArray()))]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefix), createObject('value', ''))]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefix), createObject('value', ''))]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefixes), createObject('value', createArray()))]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefixes), createObject('value', createArray()))]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceApplicationSecurityGroups), createObject('value', createArray()))]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationApplicationSecurityGroups), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "17252857850318600622" + }, + "name": "Network Security Group (NSG) Security Rules", + "description": "This module deploys a Network Security Group (NSG) Security Rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "networkSecurityGroupName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment." + } + }, + "access": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "maxLength": 140, + "metadata": { + "description": "Optional. A description for this rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "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", + "metadata": { + "description": "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", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('networkSecurityGroupName'), parameters('name'))]", + "properties": { + "access": "[parameters('access')]", + "description": "[parameters('description')]", + "destinationAddressPrefix": "[parameters('destinationAddressPrefix')]", + "destinationAddressPrefixes": "[parameters('destinationAddressPrefixes')]", + "destinationApplicationSecurityGroups": "[parameters('destinationApplicationSecurityGroups')]", + "destinationPortRange": "[parameters('destinationPortRange')]", + "destinationPortRanges": "[parameters('destinationPortRanges')]", + "direction": "[parameters('direction')]", + "priority": "[parameters('priority')]", + "protocol": "[parameters('protocol')]", + "sourceAddressPrefix": "[parameters('sourceAddressPrefix')]", + "sourceAddressPrefixes": "[parameters('sourceAddressPrefixes')]", + "sourceApplicationSecurityGroups": "[parameters('sourceApplicationSecurityGroups')]", + "sourcePortRange": "[parameters('sourcePortRange')]", + "sourcePortRanges": "[parameters('sourcePortRanges')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the security rule was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the security rule." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups/securityRules', parameters('networkSecurityGroupName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the security rule." + }, + "value": "[parameters('name')]" + } + } + } + }, + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "NSG Rules for the private enpoint subnet." + } + }, + { + "condition": "[and(not(empty(parameters('hubVNetId'))), not(empty(parameters('networkApplianceIpAddress'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('egressLockdownUdr-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.routeTable]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "routes": { + "value": [ + { + "name": "defaultEgressLockdown", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "VirtualAppliance", + "nextHopIpAddress": "[parameters('networkApplianceIpAddress')]" + } + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4697655308606864167" + }, + "name": "Route Tables", + "description": "This module deploys a User Defined Route Table (UDR).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "routeType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the route." + } + }, + "properties": { + "type": "object", + "properties": { + "nextHopType": { + "type": "string", + "allowedValues": [ + "Internet", + "None", + "VirtualAppliance", + "VirtualNetworkGateway", + "VnetLocal" + ], + "metadata": { + "description": "Required. The type of Azure hop the packet should be sent to." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination CIDR to which the route applies." + } + }, + "hasBgpOverride": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. A value indicating whether this route overrides overlapping BGP routes regardless of LPM." + } + }, + "nextHopIpAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The IP address packets should be forwarded to. Next hop values are only allowed in routes where the next hop type is VirtualAppliance." + } + } + }, + "metadata": { + "description": "Required. Properties of the route." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name given for the hub route table." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "routes": { + "$ref": "#/definitions/routeType", + "metadata": { + "description": "Optional. An array of routes to be established within the hub route table." + } + }, + "disableBgpRoutePropagation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Switch to disable BGP route propagation." + } + }, + "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 of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[take(format('46d3xbcp.res.network-routetable.{0}.{1}', replace('0.2.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "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" + } + } + } + } + }, + "routeTable": { + "type": "Microsoft.Network/routeTables", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "routes": "[parameters('routes')]", + "disableBgpRoutePropagation": "[parameters('disableBgpRoutePropagation')]" + } + }, + "routeTable_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/routeTables/{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": [ + "routeTable" + ] + }, + "routeTable_roleAssignments": { + "copy": { + "name": "routeTable_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/routeTables/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/routeTables', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "routeTable" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the route table was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the route table." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the route table." + }, + "value": "[resourceId('Microsoft.Network/routeTables', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('routeTable', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "The Route Table deployment" + } + }, + { + "condition": "[equals(parameters('vmJumpboxOSType'), 'linux')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('vm-linux-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "vmName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBox]" + }, + "bastionResourceId": { + "value": "[parameters('bastionResourceId')]" + }, + "vmAdminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "vmAdminPassword": { + "value": "[parameters('vmAdminPassword')]" + }, + "vmSshPublicKey": { + "value": "[parameters('vmLinuxSshAuthorizedKey')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "vmZone": { + "value": "[parameters('vmZone')]" + }, + "storageAccountType": { + "value": "[parameters('storageAccountType')]" + }, + "vmVnetName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value]" + }, + "vmSubnetName": { + "value": "[parameters('vmSubnetName')]" + }, + "vmSubnetAddressPrefix": { + "value": "[parameters('vmJumpBoxSubnetAddressPrefix')]" + }, + "vmNetworkInterfaceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBoxNic]" + }, + "vmNetworkSecurityGroupName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBoxNsg]" + }, + "vmAuthenticationType": { + "value": "[parameters('vmAuthenticationType')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.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.29.47.4906", + "templateHash": "15037342818970325221" + } + }, + "parameters": { + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + }, + "storageAccountType": { + "type": "string", + "defaultValue": "Standard_LRS" + }, + "vmZone": { + "type": "int", + "defaultValue": 0 + }, + "vmVnetName": { + "type": "string" + }, + "vmSubnetName": { + "type": "string" + }, + "vmSubnetAddressPrefix": { + "type": "string" + }, + "vmNetworkSecurityGroupName": { + "type": "string" + }, + "vmNetworkInterfaceName": { + "type": "string" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "bastionResourceId": { + "type": "string" + }, + "vmAdminUsername": { + "type": "string" + }, + "vmAdminPassword": { + "type": "securestring" + }, + "vmSshPublicKey": { + "type": "securestring" + }, + "vmAuthenticationType": { + "type": "string", + "defaultValue": "password", + "allowedValues": [ + "sshPublicKey", + "password" + ], + "metadata": { + "description": "Type of authentication to use on the Virtual Machine. SSH key is recommended." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName'))]", + "properties": { + "addressPrefix": "[parameters('vmSubnetAddressPrefix')]", + "networkSecurityGroup": { + "id": "[reference(resourceId('Microsoft.Resources/deployments', 'vmNetworkSecurityDeployment'), '2022-09-01').outputs.resourceId.value]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'vmNetworkSecurityDeployment')]" + ] + }, + { + "type": "Microsoft.Maintenance/maintenanceConfigurations", + "apiVersion": "2023-10-01-preview", + "name": "[format('dep-mc-{0}', parameters('vmName'))]", + "location": "[parameters('location')]", + "properties": { + "extensionProperties": { + "InGuestPatchMode": "User" + }, + "maintenanceScope": "InGuestPatch", + "maintenanceWindow": { + "startDateTime": "2024-06-16 00:00", + "duration": "03:55", + "timeZone": "W. Europe Standard Time", + "recurEvery": "1Day" + }, + "visibility": "Custom", + "installPatches": { + "rebootSetting": "IfRequired", + "linuxParameters": { + "classificationsToInclude": [ + "Critical", + "Security" + ] + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "vmNetworkSecurityDeployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('vmNetworkSecurityGroupName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "securityRules": "[if(not(empty(parameters('bastionResourceId'))), createObject('value', createArray(createObject('name', 'allow-bastion-inbound', 'properties', createObject('description', 'Allow inbound traffic from Bastion to the JumpBox', 'protocol', '*', 'sourceAddressPrefix', 'Bastion', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '*', 'access', 'Allow', 'priority', 100, 'direction', 'Inbound')))), 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.27.1.19265", + "templateHash": "8543584719514494080" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(parameters('securityRules'))]", + "input": { + "name": "[parameters('securityRules')[copyIndex('securityRules')].name]", + "properties": { + "protocol": "[parameters('securityRules')[copyIndex('securityRules')].properties.protocol]", + "access": "[parameters('securityRules')[copyIndex('securityRules')].properties.access]", + "priority": "[parameters('securityRules')[copyIndex('securityRules')].properties.priority]", + "direction": "[parameters('securityRules')[copyIndex('securityRules')].properties.direction]", + "description": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'description'), parameters('securityRules')[copyIndex('securityRules')].properties.description, '')]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRange, '')]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRanges, createArray())]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRange, '')]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRanges, createArray())]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefix, '')]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefix, '')]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefixes, createArray())]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefixes, createArray())]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceApplicationSecurityGroups, createArray())]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationApplicationSecurityGroups, 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_securityRules": { + "copy": { + "name": "networkSecurityGroup_securityRules", + "count": "[length(parameters('securityRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkSecurityGroup-SecurityRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityRules')[copyIndex()].name]" + }, + "networkSecurityGroupName": { + "value": "[parameters('name')]" + }, + "protocol": { + "value": "[parameters('securityRules')[copyIndex()].properties.protocol]" + }, + "access": { + "value": "[parameters('securityRules')[copyIndex()].properties.access]" + }, + "priority": { + "value": "[parameters('securityRules')[copyIndex()].properties.priority]" + }, + "direction": { + "value": "[parameters('securityRules')[copyIndex()].properties.direction]" + }, + "description": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'description'), createObject('value', parameters('securityRules')[copyIndex()].properties.description), createObject('value', ''))]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRange), createObject('value', ''))]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRanges), createObject('value', createArray()))]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRange), createObject('value', ''))]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRanges), createObject('value', createArray()))]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefix), createObject('value', ''))]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefix), createObject('value', ''))]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefixes), createObject('value', createArray()))]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefixes), createObject('value', createArray()))]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceApplicationSecurityGroups), createObject('value', createArray()))]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationApplicationSecurityGroups), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "17252857850318600622" + }, + "name": "Network Security Group (NSG) Security Rules", + "description": "This module deploys a Network Security Group (NSG) Security Rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "networkSecurityGroupName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment." + } + }, + "access": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "maxLength": 140, + "metadata": { + "description": "Optional. A description for this rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "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", + "metadata": { + "description": "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", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('networkSecurityGroupName'), parameters('name'))]", + "properties": { + "access": "[parameters('access')]", + "description": "[parameters('description')]", + "destinationAddressPrefix": "[parameters('destinationAddressPrefix')]", + "destinationAddressPrefixes": "[parameters('destinationAddressPrefixes')]", + "destinationApplicationSecurityGroups": "[parameters('destinationApplicationSecurityGroups')]", + "destinationPortRange": "[parameters('destinationPortRange')]", + "destinationPortRanges": "[parameters('destinationPortRanges')]", + "direction": "[parameters('direction')]", + "priority": "[parameters('priority')]", + "protocol": "[parameters('protocol')]", + "sourceAddressPrefix": "[parameters('sourceAddressPrefix')]", + "sourceAddressPrefixes": "[parameters('sourceAddressPrefixes')]", + "sourceApplicationSecurityGroups": "[parameters('sourceApplicationSecurityGroups')]", + "sourcePortRange": "[parameters('sourcePortRange')]", + "sourcePortRanges": "[parameters('sourcePortRanges')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the security rule was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the security rule." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups/securityRules', parameters('networkSecurityGroupName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the security rule." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "vmDeployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('vmName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "osType": { + "value": "Linux" + }, + "computerName": { + "value": "[parameters('vmName')]" + }, + "adminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "adminPassword": "[if(equals(parameters('vmAuthenticationType'), 'password'), createObject('value', parameters('vmAdminPassword')), createObject('value', null()))]", + "disablePasswordAuthentication": "[if(equals(parameters('vmAuthenticationType'), 'password'), createObject('value', false()), createObject('value', true()))]", + "encryptionAtHost": { + "value": false + }, + "enableAutomaticUpdates": { + "value": true + }, + "patchMode": { + "value": "AutomaticByPlatform" + }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "value": true + }, + "maintenanceConfigurationResourceId": { + "value": "[resourceId('Microsoft.Maintenance/maintenanceConfigurations', format('dep-mc-{0}', parameters('vmName')))]" + }, + "publicKeys": "[if(equals(parameters('vmAuthenticationType'), 'sshPublicKey'), createObject('value', createArray(createObject('keyData', parameters('vmSshPublicKey'), 'path', format('/home/{0}/.ssh/authorized_keys', parameters('vmAdminUsername'))))), createObject('value', createArray()))]", + "nicConfigurations": { + "value": [ + { + "name": "[parameters('vmNetworkInterfaceName')]", + "enableAcceleratedNetworking": false, + "ipConfigurations": [ + { + "name": "ipConfig01", + "privateIPAllocationMethod": "Dynamic", + "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[0], split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[1])]" + } + ] + } + ] + }, + "osDisk": { + "value": { + "caching": "ReadWrite", + "createOption": "FromImage", + "deleteOption": "Delete", + "diskSizeGB": 128, + "managedDisk": { + "storageAccountType": "[parameters('storageAccountType')]" + } + } + }, + "zone": { + "value": "[parameters('vmZone')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "imageReference": { + "value": { + "publisher": "canonical", + "offer": "0001-com-ubuntu-server-focal", + "sku": "20_04-lts-gen2", + "version": "latest" + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true, + "tags": "[parameters('tags')]", + "monitoringWorkspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7195052428088092384" + }, + "name": "Virtual Machines", + "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "osDiskType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "diskSizeGB": { + "type": "int", + "metadata": { + "description": "Required. 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." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "dataDisksType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "lun": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the logical unit number of the data disk." + } + }, + "diskSizeGB": { + "type": "int", + "metadata": { + "description": "Required. 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." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "nullable": true + } + }, + "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": "", + "metadata": { + "description": "Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings." + } + }, + "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": { + "type": "object", + "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": { + "type": "object", + "defaultValue": {}, + "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": { + "$ref": "#/definitions/dataDisksType", + "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." + } + }, + "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." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies set of certificates that should be installed onto the virtual machine." + } + }, + "priority": { + "type": "string", + "defaultValue": "Regular", + "allowedValues": [ + "Regular", + "Low", + "Spot" + ], + "metadata": { + "description": "Optional. Specifies the priority for the virtual machine." + } + }, + "enableEvictionPolicy": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of SSH public keys used to authenticate with linux based VMs." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the gallery applications that should be made available to the VM/VMSS." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set." + } + }, + "nicConfigurations": { + "type": "array", + "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": { + "type": "object", + "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": { + "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 + }, + "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": "object", + "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 identy." + } + }, + "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 identy." + } + }, + "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", + "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 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." + } + }, + "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", + "defaultValue": [], + "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." + } + }, + "winRM": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object." + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "publicKeysFormatted", + "count": "[length(parameters('publicKeys'))]", + "input": { + "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", + "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" + } + } + ], + "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'), '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(), parameters('additionalUnattendContent'))]", + "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), 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 (Preview)": "[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": "2023-07-01", + "name": "[format('46d3xbcp.res.compute-virtualmachine.{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" + } + } + } + } + }, + "vm": { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "zones": "[if(not(equals(parameters('zone'), 0)), array(string(parameters('zone'))), null())]", + "plan": "[if(not(empty(parameters('plan'))), parameters('plan'), null())]", + "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": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))]", + "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].diskSizeGB]", + "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty')]", + "deleteOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete')]", + "caching": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + } + ], + "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')]", + "diskSizeGB": "[parameters('osDisk').diskSizeGB]", + "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[parameters('osDisk').managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + }, + "additionalCapabilities": { + "ultraSSDEnabled": "[parameters('ultraSSDEnabled')]" + }, + "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": "[if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].deleteOption, 'Delete')]", + "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]" + }, + "id": "[resourceId('Microsoft.Network/networkInterfaces', if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].name, format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].nicSuffix)))]" + } + } + ] + }, + "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(parameters('enableEvictionPolicy'), 'Deallocate', 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())]" + }, + "dependsOn": [ + "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": "[if(contains(parameters('autoShutdownConfig'), 'status'), parameters('autoShutdownConfig').status, 'Disabled')]", + "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]", + "taskType": "ComputeVmShutdownTask", + "dailyRecurrence": { + "time": "[if(contains(parameters('autoShutdownConfig'), 'dailyRecurrenceTime'), parameters('autoShutdownConfig').dailyRecurrenceTime, '19:00')]" + }, + "timeZoneId": "[if(contains(parameters('autoShutdownConfig'), 'timeZone'), parameters('autoShutdownConfig').timeZone, 'UTC')]", + "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), createObject('status', if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), parameters('autoShutdownConfig').notificationStatus, 'Disabled'), 'emailRecipient', if(contains(parameters('autoShutdownConfig'), 'notificationEmail'), parameters('autoShutdownConfig').notificationEmail, ''), 'notificationLocale', if(contains(parameters('autoShutdownConfig'), 'notificationLocale'), parameters('autoShutdownConfig').notificationLocale, 'en'), 'webhookUrl', if(contains(parameters('autoShutdownConfig'), 'notificationWebhookUrl'), parameters('autoShutdownConfig').notificationWebhookUrl, ''), 'timeInMinutes', if(contains(parameters('autoShutdownConfig'), 'notificationTimeInMinutes'), parameters('autoShutdownConfig').notificationTimeInMinutes, 30)), null())]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_logAnalyticsWorkspace": { + "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "subscriptionId": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]]", + "name": "[last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))]" + }, + "AzureWindowsBaseline": { + "condition": "[not(empty(parameters('guestConfiguration')))]", + "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments", + "apiVersion": "2020-06-25", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'name'), createObject('value', parameters('nicConfigurations')[copyIndex()].name), createObject('value', format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex()].nicSuffix)))]", + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableIPForwarding": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableIPForwarding), createObject('value', false()))]", + "enableAcceleratedNetworking": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableAcceleratedNetworking), createObject('value', true()))]", + "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(parameters('nicConfigurations')[copyIndex()].dnsServers)), createObject('value', parameters('nicConfigurations')[copyIndex()].dnsServers), createObject('value', createArray())), createObject('value', createArray()))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('nicConfigurations')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "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": "[coalesce(tryGet(parameters('nicConfigurations')[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.28.1.47646", + "templateHash": "6185170122493883924" + } + }, + "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." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "networkInterfaceName": { + "type": "string" + }, + "virtualMachineName": { + "type": "string" + }, + "ipConfigurations": { + "type": "array" + }, + "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", + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the Network Interface." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "networkInterface_publicIPAddresses": { + "copy": { + "name": "networkInterface_publicIPAddresses", + "count": "[length(parameters('ipConfigurations'))]" + }, + "condition": "[contains(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'name'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.name), createObject('value', format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIpNameSuffix)))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "idleTimeoutInMinutes": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'idleTimeoutInMinutes')]" + }, + "ddosSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'ddosSettings')]" + }, + "dnsSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'dnsSettings')]" + }, + "publicIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAddressVersion'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAddressVersion), createObject('value', 'IPv4'))]", + "publicIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAllocationMethod'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAllocationMethod), createObject('value', 'Static'))]", + "publicIpPrefixResourceId": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPPrefixResourceId'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPPrefixResourceId), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'roleAssignments'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.roleAssignments), createObject('value', createArray()))]", + "skuName": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuName'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuName), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuTier'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuTier), createObject('value', 'Regional'))]", + "tags": { + "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "zones": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'zones'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.zones), createObject('value', createArray(1, 2, 3)))]", + "enableTelemetry": { + "value": "[coalesce(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.27.1.19265", + "templateHash": "10356333973104369631" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "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." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{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" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-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": null + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-09-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', if(not(empty(parameters('ipConfigurations')[copyIndex('value')].name)), parameters('ipConfigurations')[copyIndex('value')].name, null()), 'primary', equals(copyIndex('value'), 0), 'privateIPAllocationMethod', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod, null()), null()), 'privateIPAddress', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('value')].privateIPAddress, null()), null()), 'publicIPAddressResourceId', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), resourceId('Microsoft.Network/publicIPAddresses', if(contains(parameters('ipConfigurations')[copyIndex('value')].pipConfiguration, 'name'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.name, format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.publicIpNameSuffix))), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerBackendAddressPools, null()), 'applicationSecurityGroups', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('value')].applicationSecurityGroups, null()), 'applicationGatewayBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].applicationGatewayBackendAddressPools, null()), 'gatewayLoadBalancer', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('value')].gatewayLoadBalancer, null()), 'loadBalancerInboundNatRules', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerInboundNatRules, null()), 'privateIPAddressVersion', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('value')].privateIPAddressVersion, null()), 'virtualNetworkTaps', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('value')].virtualNetworkTaps, null()))]" + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": "[parameters('diagnosticSettings')]" + }, + "dnsServers": "[if(not(empty(parameters('dnsServers'))), createObject('value', parameters('dnsServers')), createObject('value', createArray()))]", + "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": "[if(not(empty(parameters('roleAssignments'))), createObject('value', parameters('roleAssignments')), 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.25.53.49325", + "templateHash": "1612343535299711142" + }, + "name": "Network Interface", + "description": "This module deploys a Network Interface.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + } + }, + "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", + "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", + "metadata": { + "description": "Required. A list of IPConfigurations of the network interface." + } + }, + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.2.4', '.', '-'), 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": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "ipConfigurations", + "count": "[length(parameters('ipConfigurations'))]", + "input": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'name'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].name, format('ipconfig0{0}', add(copyIndex('ipConfigurations'), 1)))]", + "properties": { + "primary": "[if(equals(copyIndex('ipConfigurations'), 0), true(), false())]", + "privateIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod, null()), null())]", + "privateIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress, null()), null())]", + "publicIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), if(not(equals(parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId, null())), createObject('id', parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId), null()), null())]", + "subnet": { + "id": "[parameters('ipConfigurations')[copyIndex('ipConfigurations')].subnetResourceId]" + }, + "loadBalancerBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerBackendAddressPools, null())]", + "applicationSecurityGroups": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationSecurityGroups, null())]", + "applicationGatewayBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationGatewayBackendAddressPools, null())]", + "gatewayLoadBalancer": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].gatewayLoadBalancer, null())]", + "loadBalancerInboundNatRules": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerInboundNatRules, null())]", + "privateIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddressVersion, null())]", + "virtualNetworkTaps": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].virtualNetworkTaps, null())]" + } + } + } + ], + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkInterfaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "networkInterface_publicIPAddresses" + ] + } + } + } + } + }, + "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": "AADLogin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.ActiveDirectory" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AADLoginForWindows'), createObject('value', 'AADSSHLoginforLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAadJoinConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAadJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAadJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAadJoinConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionAadJoinConfig'), 'settings'), createObject('value', parameters('extensionAadJoinConfig').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": { + "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": "DomainJoin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Compute" + }, + "type": { + "value": "JsonADDomainExtension" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDomainJoinConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDomainJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDomainJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDomainJoinConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "MicrosoftAntiMalware" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": { + "value": "IaaSAntimalware" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAntiMalwareConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAntiMalwareConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAntiMalwareConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAntiMalwareConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": { + "value": "[parameters('extensionAntiMalwareConfig').settings]" + }, + "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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "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())]" + }, + "settings": { + "value": { + "workspaceId": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), reference('vm_logAnalyticsWorkspace').customerId, '')]", + "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" + } + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": { + "workspaceKey": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2], split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_logAnalyticsWorkspace", + "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": "DependencyAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Monitoring.DependencyAgent" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDependencyAgentConfig').typeHandlerVersion), createObject('value', '9.10'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDependencyAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDependencyAgentConfig').enableAutomaticUpgrade), createObject('value', true()))]", + "settings": { + "value": { + "enableAMA": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAMA'), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NetworkWatcherAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.NetworkWatcher" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'NetworkWatcherAgentWindows'), createObject('value', 'NetworkWatcherAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "DesiredStateConfiguration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Powershell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDSCConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDSCConfig').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDSCConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDSCConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDSCConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDSCConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionDSCConfig'), 'settings'), createObject('value', parameters('extensionDSCConfig').settings), createObject('value', createObject()))]", + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": "[if(contains(parameters('extensionDSCConfig'), 'protectedSettings'), createObject('value', parameters('extensionDSCConfig').protectedSettings), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "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": "[if(contains(parameters('extensionCustomScriptConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionCustomScriptConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.10'), createObject('value', '2.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionCustomScriptConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionCustomScriptConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionCustomScriptConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionCustomScriptConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "AzureDiskEncryption" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureDiskEncryption'), createObject('value', 'AzureDiskEncryptionForLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.2'), createObject('value', '1.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "forceUpdateTag": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'forceUpdateTag'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').forceUpdateTag), createObject('value', '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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NvidiaGpuDriverWindows" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.HpcCompute" + }, + "type": { + "value": "NvidiaGpuDriverWindows" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'typeHandlerVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "HostPoolRegistration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.PowerShell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'typeHandlerVersion'), createObject('value', parameters('extensionHostPoolRegistration').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionHostPoolRegistration').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionHostPoolRegistration').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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(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": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'typeHandlerVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionGuestConfigurationExtension').enableAutomaticUpgrade), createObject('value', true()))]", + "forceUpdateTag": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'forceUpdateTag'), createObject('value', parameters('extensionGuestConfigurationExtension').forceUpdateTag), createObject('value', '1.0'))]", + "settings": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'settings'), createObject('value', parameters('extensionGuestConfigurationExtension').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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('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.28.1.47646", + "templateHash": "4311417160342913977" + }, + "name": "Recovery Service Vaults Protection Container Protected Item", + "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", + "owner": "Azure/module-maintainers" + }, + "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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('vm', '2023-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Maintenance/maintenanceConfigurations', format('dep-mc-{0}', parameters('vmName')))]", + "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[0], split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[1])]" + ] + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "An optional Linux virtual machine deployment to act as a jump box." + } + }, + { + "condition": "[equals(parameters('vmJumpboxOSType'), 'windows')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('vm-windows-{0}', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "vmName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBox]" + }, + "bastionResourceId": { + "value": "[parameters('bastionResourceId')]" + }, + "vmAdminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "vmAdminPassword": { + "value": "[parameters('vmAdminPassword')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "vmZone": { + "value": "[parameters('vmZone')]" + }, + "storageAccountType": { + "value": "[parameters('storageAccountType')]" + }, + "vmVnetName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value]" + }, + "vmSubnetName": { + "value": "[parameters('vmSubnetName')]" + }, + "vmSubnetAddressPrefix": { + "value": "[parameters('vmJumpBoxSubnetAddressPrefix')]" + }, + "vmNetworkInterfaceName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBoxNic]" + }, + "vmNetworkSecurityGroupName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBoxNsg]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.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.29.47.4906", + "templateHash": "8661291685866182643" + } + }, + "parameters": { + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + }, + "storageAccountType": { + "type": "string", + "defaultValue": "Standard_LRS" + }, + "vmWindowsOSVersion": { + "type": "string", + "defaultValue": "2016-Datacenter" + }, + "vmZone": { + "type": "int", + "defaultValue": 0 + }, + "vmVnetName": { + "type": "string" + }, + "vmSubnetName": { + "type": "string" + }, + "vmSubnetAddressPrefix": { + "type": "string" + }, + "vmNetworkSecurityGroupName": { + "type": "string" + }, + "vmNetworkInterfaceName": { + "type": "string" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "bastionResourceId": { + "type": "string" + }, + "vmAdminUsername": { + "type": "string" + }, + "vmAdminPassword": { + "type": "securestring" + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName'))]", + "properties": { + "addressPrefix": "[parameters('vmSubnetAddressPrefix')]", + "networkSecurityGroup": { + "id": "[reference(resourceId('Microsoft.Resources/deployments', 'vmNetworkSecurityDeployment'), '2022-09-01').outputs.resourceId.value]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'vmNetworkSecurityDeployment')]" + ] + }, + { + "type": "Microsoft.Maintenance/maintenanceConfigurations", + "apiVersion": "2023-10-01-preview", + "name": "[format('dep-mc-{0}', parameters('vmName'))]", + "location": "[parameters('location')]", + "properties": { + "extensionProperties": { + "InGuestPatchMode": "User" + }, + "maintenanceScope": "InGuestPatch", + "maintenanceWindow": { + "startDateTime": "2024-06-16 00:00", + "duration": "03:55", + "timeZone": "W. Europe Standard Time", + "recurEvery": "1Day" + }, + "visibility": "Custom", + "installPatches": { + "rebootSetting": "IfRequired", + "windowsParameters": { + "classificationsToInclude": [ + "Critical", + "Security" + ] + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "vmNetworkSecurityDeployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('vmNetworkSecurityGroupName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "securityRules": "[if(not(empty(parameters('bastionResourceId'))), createObject('value', createArray(createObject('name', 'allow-bastion-inbound', 'properties', createObject('description', 'Allow inbound traffic from Bastion to the JumpBox', 'protocol', '*', 'sourceAddressPrefix', 'Bastion', 'sourcePortRange', '*', 'destinationAddressPrefix', '*', 'destinationPortRange', '*', 'access', 'Allow', 'priority', 100, 'direction', 'Inbound')))), 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.27.1.19265", + "templateHash": "8543584719514494080" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(parameters('securityRules'))]", + "input": { + "name": "[parameters('securityRules')[copyIndex('securityRules')].name]", + "properties": { + "protocol": "[parameters('securityRules')[copyIndex('securityRules')].properties.protocol]", + "access": "[parameters('securityRules')[copyIndex('securityRules')].properties.access]", + "priority": "[parameters('securityRules')[copyIndex('securityRules')].properties.priority]", + "direction": "[parameters('securityRules')[copyIndex('securityRules')].properties.direction]", + "description": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'description'), parameters('securityRules')[copyIndex('securityRules')].properties.description, '')]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRange, '')]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourcePortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.sourcePortRanges, createArray())]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRange'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRange, '')]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationPortRanges'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationPortRanges, createArray())]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefix, '')]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefix, '')]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceAddressPrefixes, createArray())]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationAddressPrefixes, createArray())]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.sourceApplicationSecurityGroups, createArray())]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroups'), parameters('securityRules')[copyIndex('securityRules')].properties.destinationApplicationSecurityGroups, 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_securityRules": { + "copy": { + "name": "networkSecurityGroup_securityRules", + "count": "[length(parameters('securityRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkSecurityGroup-SecurityRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityRules')[copyIndex()].name]" + }, + "networkSecurityGroupName": { + "value": "[parameters('name')]" + }, + "protocol": { + "value": "[parameters('securityRules')[copyIndex()].properties.protocol]" + }, + "access": { + "value": "[parameters('securityRules')[copyIndex()].properties.access]" + }, + "priority": { + "value": "[parameters('securityRules')[copyIndex()].properties.priority]" + }, + "direction": { + "value": "[parameters('securityRules')[copyIndex()].properties.direction]" + }, + "description": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'description'), createObject('value', parameters('securityRules')[copyIndex()].properties.description), createObject('value', ''))]", + "sourcePortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRange), createObject('value', ''))]", + "sourcePortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourcePortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourcePortRanges), createObject('value', createArray()))]", + "destinationPortRange": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRange'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRange), createObject('value', ''))]", + "destinationPortRanges": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationPortRanges'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationPortRanges), createObject('value', createArray()))]", + "sourceAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefix), createObject('value', ''))]", + "destinationAddressPrefix": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefix'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefix), createObject('value', ''))]", + "sourceAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceAddressPrefixes), createObject('value', createArray()))]", + "destinationAddressPrefixes": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationAddressPrefixes'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationAddressPrefixes), createObject('value', createArray()))]", + "sourceApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'sourceApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.sourceApplicationSecurityGroups), createObject('value', createArray()))]", + "destinationApplicationSecurityGroups": "[if(contains(parameters('securityRules')[copyIndex()].properties, 'destinationApplicationSecurityGroups'), createObject('value', parameters('securityRules')[copyIndex()].properties.destinationApplicationSecurityGroups), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "17252857850318600622" + }, + "name": "Network Security Group (NSG) Security Rules", + "description": "This module deploys a Network Security Group (NSG) Security Rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "networkSecurityGroupName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment." + } + }, + "access": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "maxLength": 140, + "metadata": { + "description": "Optional. A description for this rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "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", + "metadata": { + "description": "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", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The application security group specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups/securityRules", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('networkSecurityGroupName'), parameters('name'))]", + "properties": { + "access": "[parameters('access')]", + "description": "[parameters('description')]", + "destinationAddressPrefix": "[parameters('destinationAddressPrefix')]", + "destinationAddressPrefixes": "[parameters('destinationAddressPrefixes')]", + "destinationApplicationSecurityGroups": "[parameters('destinationApplicationSecurityGroups')]", + "destinationPortRange": "[parameters('destinationPortRange')]", + "destinationPortRanges": "[parameters('destinationPortRanges')]", + "direction": "[parameters('direction')]", + "priority": "[parameters('priority')]", + "protocol": "[parameters('protocol')]", + "sourceAddressPrefix": "[parameters('sourceAddressPrefix')]", + "sourceAddressPrefixes": "[parameters('sourceAddressPrefixes')]", + "sourceApplicationSecurityGroups": "[parameters('sourceApplicationSecurityGroups')]", + "sourcePortRange": "[parameters('sourcePortRange')]", + "sourcePortRanges": "[parameters('sourcePortRanges')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the security rule was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the security rule." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups/securityRules', parameters('networkSecurityGroupName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the security rule." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "vmDeployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('vmName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "osType": { + "value": "Windows" + }, + "computerName": { + "value": "[parameters('vmName')]" + }, + "adminUsername": { + "value": "[parameters('vmAdminUsername')]" + }, + "adminPassword": { + "value": "[parameters('vmAdminPassword')]" + }, + "encryptionAtHost": { + "value": false + }, + "enableAutomaticUpdates": { + "value": true + }, + "patchMode": { + "value": "AutomaticByPlatform" + }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "value": true + }, + "maintenanceConfigurationResourceId": { + "value": "[resourceId('Microsoft.Maintenance/maintenanceConfigurations', format('dep-mc-{0}', parameters('vmName')))]" + }, + "nicConfigurations": { + "value": [ + { + "name": "[parameters('vmNetworkInterfaceName')]", + "enableAcceleratedNetworking": false, + "ipConfigurations": [ + { + "name": "ipconfig1", + "privateIPAllocationMethod": "Dynamic", + "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[0], split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[1])]" + } + ] + } + ] + }, + "osDisk": { + "value": { + "caching": "ReadWrite", + "createOption": "FromImage", + "deleteOption": "Delete", + "diskSizeGB": 128, + "managedDisk": { + "storageAccountType": "[parameters('storageAccountType')]" + } + } + }, + "zone": { + "value": "[parameters('vmZone')]" + }, + "vmSize": { + "value": "[parameters('vmSize')]" + }, + "imageReference": { + "value": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "[parameters('vmWindowsOSVersion')]", + "version": "latest" + } + }, + "extensionMonitoringAgentConfig": { + "value": { + "enabled": true, + "tags": "[parameters('tags')]", + "monitoringWorkspaceResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7195052428088092384" + }, + "name": "Virtual Machines", + "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "osDiskType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "diskSizeGB": { + "type": "int", + "metadata": { + "description": "Required. 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." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "dataDisksType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "lun": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the logical unit number of the data disk." + } + }, + "diskSizeGB": { + "type": "int", + "metadata": { + "description": "Required. 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." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "nullable": true + } + }, + "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": "", + "metadata": { + "description": "Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings." + } + }, + "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": { + "type": "object", + "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": { + "type": "object", + "defaultValue": {}, + "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": { + "$ref": "#/definitions/dataDisksType", + "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." + } + }, + "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." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies set of certificates that should be installed onto the virtual machine." + } + }, + "priority": { + "type": "string", + "defaultValue": "Regular", + "allowedValues": [ + "Regular", + "Low", + "Spot" + ], + "metadata": { + "description": "Optional. Specifies the priority for the virtual machine." + } + }, + "enableEvictionPolicy": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of SSH public keys used to authenticate with linux based VMs." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the gallery applications that should be made available to the VM/VMSS." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set." + } + }, + "nicConfigurations": { + "type": "array", + "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": { + "type": "object", + "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": { + "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 + }, + "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": "object", + "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 identy." + } + }, + "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 identy." + } + }, + "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", + "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 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." + } + }, + "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", + "defaultValue": [], + "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." + } + }, + "winRM": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object." + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "publicKeysFormatted", + "count": "[length(parameters('publicKeys'))]", + "input": { + "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", + "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" + } + } + ], + "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'), '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(), parameters('additionalUnattendContent'))]", + "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), 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 (Preview)": "[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": "2023-07-01", + "name": "[format('46d3xbcp.res.compute-virtualmachine.{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" + } + } + } + } + }, + "vm": { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "zones": "[if(not(equals(parameters('zone'), 0)), array(string(parameters('zone'))), null())]", + "plan": "[if(not(empty(parameters('plan'))), parameters('plan'), null())]", + "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": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))]", + "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].diskSizeGB]", + "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty')]", + "deleteOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete')]", + "caching": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + } + ], + "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')]", + "diskSizeGB": "[parameters('osDisk').diskSizeGB]", + "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[parameters('osDisk').managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + }, + "additionalCapabilities": { + "ultraSSDEnabled": "[parameters('ultraSSDEnabled')]" + }, + "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": "[if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].deleteOption, 'Delete')]", + "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]" + }, + "id": "[resourceId('Microsoft.Network/networkInterfaces', if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].name, format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].nicSuffix)))]" + } + } + ] + }, + "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(parameters('enableEvictionPolicy'), 'Deallocate', 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())]" + }, + "dependsOn": [ + "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": "[if(contains(parameters('autoShutdownConfig'), 'status'), parameters('autoShutdownConfig').status, 'Disabled')]", + "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]", + "taskType": "ComputeVmShutdownTask", + "dailyRecurrence": { + "time": "[if(contains(parameters('autoShutdownConfig'), 'dailyRecurrenceTime'), parameters('autoShutdownConfig').dailyRecurrenceTime, '19:00')]" + }, + "timeZoneId": "[if(contains(parameters('autoShutdownConfig'), 'timeZone'), parameters('autoShutdownConfig').timeZone, 'UTC')]", + "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), createObject('status', if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), parameters('autoShutdownConfig').notificationStatus, 'Disabled'), 'emailRecipient', if(contains(parameters('autoShutdownConfig'), 'notificationEmail'), parameters('autoShutdownConfig').notificationEmail, ''), 'notificationLocale', if(contains(parameters('autoShutdownConfig'), 'notificationLocale'), parameters('autoShutdownConfig').notificationLocale, 'en'), 'webhookUrl', if(contains(parameters('autoShutdownConfig'), 'notificationWebhookUrl'), parameters('autoShutdownConfig').notificationWebhookUrl, ''), 'timeInMinutes', if(contains(parameters('autoShutdownConfig'), 'notificationTimeInMinutes'), parameters('autoShutdownConfig').notificationTimeInMinutes, 30)), null())]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_logAnalyticsWorkspace": { + "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "subscriptionId": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]]", + "name": "[last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))]" + }, + "AzureWindowsBaseline": { + "condition": "[not(empty(parameters('guestConfiguration')))]", + "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments", + "apiVersion": "2020-06-25", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'name'), createObject('value', parameters('nicConfigurations')[copyIndex()].name), createObject('value', format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex()].nicSuffix)))]", + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableIPForwarding": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableIPForwarding), createObject('value', false()))]", + "enableAcceleratedNetworking": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableAcceleratedNetworking), createObject('value', true()))]", + "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(parameters('nicConfigurations')[copyIndex()].dnsServers)), createObject('value', parameters('nicConfigurations')[copyIndex()].dnsServers), createObject('value', createArray())), createObject('value', createArray()))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('nicConfigurations')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "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": "[coalesce(tryGet(parameters('nicConfigurations')[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.28.1.47646", + "templateHash": "6185170122493883924" + } + }, + "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." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "networkInterfaceName": { + "type": "string" + }, + "virtualMachineName": { + "type": "string" + }, + "ipConfigurations": { + "type": "array" + }, + "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", + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the Network Interface." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "networkInterface_publicIPAddresses": { + "copy": { + "name": "networkInterface_publicIPAddresses", + "count": "[length(parameters('ipConfigurations'))]" + }, + "condition": "[contains(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'name'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.name), createObject('value', format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIpNameSuffix)))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "idleTimeoutInMinutes": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'idleTimeoutInMinutes')]" + }, + "ddosSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'ddosSettings')]" + }, + "dnsSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'dnsSettings')]" + }, + "publicIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAddressVersion'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAddressVersion), createObject('value', 'IPv4'))]", + "publicIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAllocationMethod'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAllocationMethod), createObject('value', 'Static'))]", + "publicIpPrefixResourceId": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPPrefixResourceId'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPPrefixResourceId), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'roleAssignments'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.roleAssignments), createObject('value', createArray()))]", + "skuName": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuName'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuName), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuTier'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuTier), createObject('value', 'Regional'))]", + "tags": { + "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "zones": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'zones'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.zones), createObject('value', createArray(1, 2, 3)))]", + "enableTelemetry": { + "value": "[coalesce(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.27.1.19265", + "templateHash": "10356333973104369631" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "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." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{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" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-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": null + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-09-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', if(not(empty(parameters('ipConfigurations')[copyIndex('value')].name)), parameters('ipConfigurations')[copyIndex('value')].name, null()), 'primary', equals(copyIndex('value'), 0), 'privateIPAllocationMethod', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod, null()), null()), 'privateIPAddress', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('value')].privateIPAddress, null()), null()), 'publicIPAddressResourceId', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), resourceId('Microsoft.Network/publicIPAddresses', if(contains(parameters('ipConfigurations')[copyIndex('value')].pipConfiguration, 'name'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.name, format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.publicIpNameSuffix))), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerBackendAddressPools, null()), 'applicationSecurityGroups', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('value')].applicationSecurityGroups, null()), 'applicationGatewayBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].applicationGatewayBackendAddressPools, null()), 'gatewayLoadBalancer', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('value')].gatewayLoadBalancer, null()), 'loadBalancerInboundNatRules', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerInboundNatRules, null()), 'privateIPAddressVersion', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('value')].privateIPAddressVersion, null()), 'virtualNetworkTaps', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('value')].virtualNetworkTaps, null()))]" + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": "[parameters('diagnosticSettings')]" + }, + "dnsServers": "[if(not(empty(parameters('dnsServers'))), createObject('value', parameters('dnsServers')), createObject('value', createArray()))]", + "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": "[if(not(empty(parameters('roleAssignments'))), createObject('value', parameters('roleAssignments')), 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.25.53.49325", + "templateHash": "1612343535299711142" + }, + "name": "Network Interface", + "description": "This module deploys a Network Interface.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + } + }, + "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", + "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", + "metadata": { + "description": "Required. A list of IPConfigurations of the network interface." + } + }, + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.2.4', '.', '-'), 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": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "ipConfigurations", + "count": "[length(parameters('ipConfigurations'))]", + "input": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'name'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].name, format('ipconfig0{0}', add(copyIndex('ipConfigurations'), 1)))]", + "properties": { + "primary": "[if(equals(copyIndex('ipConfigurations'), 0), true(), false())]", + "privateIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod, null()), null())]", + "privateIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress, null()), null())]", + "publicIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), if(not(equals(parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId, null())), createObject('id', parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId), null()), null())]", + "subnet": { + "id": "[parameters('ipConfigurations')[copyIndex('ipConfigurations')].subnetResourceId]" + }, + "loadBalancerBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerBackendAddressPools, null())]", + "applicationSecurityGroups": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationSecurityGroups, null())]", + "applicationGatewayBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationGatewayBackendAddressPools, null())]", + "gatewayLoadBalancer": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].gatewayLoadBalancer, null())]", + "loadBalancerInboundNatRules": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerInboundNatRules, null())]", + "privateIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddressVersion, null())]", + "virtualNetworkTaps": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].virtualNetworkTaps, null())]" + } + } + } + ], + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkInterfaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "networkInterface_publicIPAddresses" + ] + } + } + } + } + }, + "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": "AADLogin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.ActiveDirectory" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AADLoginForWindows'), createObject('value', 'AADSSHLoginforLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAadJoinConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAadJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAadJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAadJoinConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionAadJoinConfig'), 'settings'), createObject('value', parameters('extensionAadJoinConfig').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": { + "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": "DomainJoin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Compute" + }, + "type": { + "value": "JsonADDomainExtension" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDomainJoinConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDomainJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDomainJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDomainJoinConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "MicrosoftAntiMalware" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": { + "value": "IaaSAntimalware" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAntiMalwareConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAntiMalwareConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAntiMalwareConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAntiMalwareConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": { + "value": "[parameters('extensionAntiMalwareConfig').settings]" + }, + "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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "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())]" + }, + "settings": { + "value": { + "workspaceId": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), reference('vm_logAnalyticsWorkspace').customerId, '')]", + "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" + } + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": { + "workspaceKey": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2], split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_logAnalyticsWorkspace", + "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": "DependencyAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Monitoring.DependencyAgent" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDependencyAgentConfig').typeHandlerVersion), createObject('value', '9.10'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDependencyAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDependencyAgentConfig').enableAutomaticUpgrade), createObject('value', true()))]", + "settings": { + "value": { + "enableAMA": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAMA'), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NetworkWatcherAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.NetworkWatcher" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'NetworkWatcherAgentWindows'), createObject('value', 'NetworkWatcherAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "DesiredStateConfiguration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Powershell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDSCConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDSCConfig').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDSCConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDSCConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDSCConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDSCConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionDSCConfig'), 'settings'), createObject('value', parameters('extensionDSCConfig').settings), createObject('value', createObject()))]", + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": "[if(contains(parameters('extensionDSCConfig'), 'protectedSettings'), createObject('value', parameters('extensionDSCConfig').protectedSettings), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "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": "[if(contains(parameters('extensionCustomScriptConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionCustomScriptConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.10'), createObject('value', '2.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionCustomScriptConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionCustomScriptConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionCustomScriptConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionCustomScriptConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "AzureDiskEncryption" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureDiskEncryption'), createObject('value', 'AzureDiskEncryptionForLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.2'), createObject('value', '1.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "forceUpdateTag": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'forceUpdateTag'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').forceUpdateTag), createObject('value', '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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NvidiaGpuDriverWindows" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.HpcCompute" + }, + "type": { + "value": "NvidiaGpuDriverWindows" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'typeHandlerVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "HostPoolRegistration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.PowerShell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'typeHandlerVersion'), createObject('value', parameters('extensionHostPoolRegistration').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionHostPoolRegistration').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionHostPoolRegistration').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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(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": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'typeHandlerVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionGuestConfigurationExtension').enableAutomaticUpgrade), createObject('value', true()))]", + "forceUpdateTag": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'forceUpdateTag'), createObject('value', parameters('extensionGuestConfigurationExtension').forceUpdateTag), createObject('value', '1.0'))]", + "settings": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'settings'), createObject('value', parameters('extensionGuestConfigurationExtension').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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('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.28.1.47646", + "templateHash": "4311417160342913977" + }, + "name": "Recovery Service Vaults Protection Container Protected Item", + "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", + "owner": "Azure/module-maintainers" + }, + "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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('vm', '2023-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Maintenance/maintenanceConfigurations', format('dep-mc-{0}', parameters('vmName')))]", + "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[0], split(format('{0}/{1}', parameters('vmVnetName'), parameters('vmSubnetName')), '/')[1])]" + ] + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "An optional Windows virtual machine deployment to act as a jump box." + } + } + ], + "outputs": { + "spokeResourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the spoke resource group." + }, + "value": "[take(format('rg-{0}', deployment().name), 64)]" + }, + "spokeVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the spoke virtual network." + }, + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Network/virtualNetworks', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value)]" + }, + "spokeVNetName": { + "type": "string", + "metadata": { + "description": "The name of the spoke virtual network." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value]" + }, + "spokeInfraSubnetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the spoke infrastructure subnet." + }, + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Network/virtualNetworks/subnets', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value, parameters('spokeInfraSubnetName'))]" + }, + "spokeInfraSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the spoke infrastructure subnet." + }, + "value": "[parameters('spokeInfraSubnetName')]" + }, + "spokePrivateEndpointsSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the spoke private endpoints subnet." + }, + "value": "[parameters('spokePrivateEndpointsSubnetName')]" + }, + "spokeApplicationGatewaySubnetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the spoke Application Gateway subnet. This is '' if the subnet was not created." + }, + "value": "[if(not(empty(parameters('spokeApplicationGatewaySubnetAddressPrefix'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Network/virtualNetworks/subnets', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('vnetSpoke-{0}', deployment().name), 64)), '2022-09-01').outputs.name.value, parameters('spokeApplicationGatewaySubnetName')), '')]" + }, + "spokeApplicationGatewaySubnetName": { + "type": "string", + "metadata": { + "description": "The name of the spoke Application Gateway subnet. This is '' if the subnet was not created." + }, + "value": "[if(not(empty(parameters('spokeApplicationGatewaySubnetAddressPrefix'))), parameters('spokeApplicationGatewaySubnetName'), '')]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Log Analytics Workspace." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('logAnalyticsWs-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "vmJumpBoxName": { + "type": "string", + "metadata": { + "description": "The name of the jump box virtual machine." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('spokeNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.vmJumpBox]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('supportingServices-{0}-deployment', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "spokePrivateEndpointSubnetName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokePrivateEndpointsSubnetName.value]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "spokeVNetId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeVNetId.value]" + }, + "hubVNetId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.logAnalyticsWorkspaceId.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.29.47.4906", + "templateHash": "4540495316729936890" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created. This needs to be the same region as the spoke." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deployment telemetry." + } + }, + "hubVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing hub virtual network." + } + }, + "spokeVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing spoke virtual network to which the private endpoint will be connected." + } + }, + "spokePrivateEndpointSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the existing subnet in the spoke virtual to which the private endpoint will be connected." + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. If left empty, no diagnostics settings will be defined." + } + }, + "deployZoneRedundantResources": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(resourceGroup().id)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + }, + "metadata": { + "description": "User-configured naming rules" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('containerRegistryModule-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "containerRegistryName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.containerRegistry]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "spokeVNetId": { + "value": "[parameters('spokeVNetId')]" + }, + "hubVNetId": { + "value": "[parameters('hubVNetId')]" + }, + "spokePrivateEndpointSubnetName": { + "value": "[parameters('spokePrivateEndpointSubnetName')]" + }, + "containerRegistryPrivateEndpointName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.containerRegistryPep]" + }, + "containerRegistryUserAssignedIdentityName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.containerRegistryUserAssignedIdentity]" + }, + "diagnosticWorkspaceId": { + "value": "[parameters('logAnalyticsWorkspaceId')]" + }, + "deployZoneRedundantResources": { + "value": "[parameters('deployZoneRedundantResources')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1772796700946624265" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created." + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name of the container registry." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "hubVNetId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the Hub Virtual Network." + } + }, + "spokeVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the VNet to which the private endpoint will be connected." + } + }, + "spokePrivateEndpointSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the subnet in the VNet to which the private endpoint will be connected." + } + }, + "containerRegistryPrivateEndpointName": { + "type": "string", + "defaultValue": "acr-pep", + "metadata": { + "description": "Optional. The name of the private endpoint to be created for Azure Container Registry. If left empty, it defaults to \"-pep" + } + }, + "containerRegistryUserAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity to be created to pull image from Azure Container Registry." + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the diagnostic log analytics workspace." + } + }, + "deployZoneRedundantResources": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false." + } + } + }, + "variables": { + "acrDnsZoneName": "privatelink.azurecr.io", + "spokeVNetIdTokens": "[split(parameters('spokeVNetId'), '/')]", + "spokeSubscriptionId": "[variables('spokeVNetIdTokens')[2]]", + "spokeResourceGroupName": "[variables('spokeVNetIdTokens')[4]]", + "spokeVNetName": "[variables('spokeVNetIdTokens')[8]]", + "containerRegistryPullRoleGuid": "7f951dda-4ed3-4680-a7ca-43fe172d538d", + "virtualNetworkLinks": "[concat(createArray(createObject('virtualNetworkResourceId', parameters('spokeVNetId'), 'registrationEnabled', false())), if(not(empty(parameters('hubVNetId'))), createArray(createObject('virtualNetworkResourceId', parameters('hubVNetId'), 'registrationEnabled', false())), createArray()))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('containerRegistryUserAssignedIdentityName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryUserAssignedIdentityName')]" + }, + "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.26.54.24096", + "templateHash": "998408512764899332" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity.", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "federatedIdentityCredentialsType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/federatedIdentityCredentialsType", + "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", + "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 of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.2.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": "2023-01-31", + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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-FederatedIdentityCredential-{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.26.54.24096", + "templateHash": "4317497001099502136" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential.", + "owner": "Azure/module-maintainers" + }, + "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": "2023-01-31", + "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', '2023-01-31', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('acrDnsZoneDeployment-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('acrDnsZoneName')]" + }, + "location": { + "value": "global" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "virtualNetworkLinks": { + "value": "[variables('virtualNetworkLinks')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "11412633288570781407" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.3.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8657463061873174631" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1725749611610769138" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "12750497126800708872" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "13512848802819943036" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "5069219418696231980" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8649693365938652763" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1580431299497536987" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1628766837655887758" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8423159394395901832" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "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', '2020-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]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('containerRegistry-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "acrSku": { + "value": "Premium" + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "acrAdminUserEnabled": { + "value": false + }, + "networkRuleBypassOptions": { + "value": "AzureServices" + }, + "zoneRedundancy": "[if(parameters('deployZoneRedundantResources'), createObject('value', 'Enabled'), createObject('value', 'Disabled'))]", + "trustPolicyStatus": { + "value": "enabled" + }, + "diagnosticSettings": { + "value": [ + { + "name": "acr-log-analytics", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "workspaceResourceId": "[parameters('diagnosticWorkspaceId')]" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "name": "[parameters('containerRegistryPrivateEndpointName')]", + "privateDnsZoneResourceIds": [ + "[reference(resourceId('Microsoft.Resources/deployments', format('acrDnsZoneDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.resourceId.value]" + ], + "subnetResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('spokeSubscriptionId'), variables('spokeResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', variables('spokeVNetName'), parameters('spokePrivateEndpointSubnetName'))]" + } + ] + }, + "quarantinePolicyStatus": { + "value": "enabled" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference(resourceId('Microsoft.Resources/deployments', parameters('containerRegistryUserAssignedIdentityName')), '2022-09-01').outputs.principalId.value]", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "[variables('containerRegistryPullRoleGuid')]" + } + ] + }, + "softDeletePolicyDays": { + "value": 7 + }, + "softDeletePolicyStatus": { + "value": "disabled" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "16326657109833349577" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of your Azure container registry." + } + }, + "exportPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the export policy is enabled or not." + } + }, + "quarantinePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "trustPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the retention policy is enabled or not." + } + }, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, + "metadata": { + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." + } + }, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled." + } + }, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. Soft Delete policy status. Default is disabled." + } + }, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "allowedValues": [ + "AzureServices", + "None" + ], + "metadata": { + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." + } + }, + "networkRuleSetDefaultAction": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. The default action of allow or deny when no other rules match." + } + }, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + }, + "replications": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All replications to create." + } + }, + "webhooks": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All webhooks to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "cacheRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview))." + } + } + }, + "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": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.3.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, + "properties": { + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } + }, + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "registry_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { + "copy": { + "name": "registry_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "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": [ + "registry" + ] + }, + "registry_roleAssignments": { + "copy": { + "name": "registry_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "13769087012819677314" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "registryName": { + "value": "[parameters('name')]" + }, + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name'), replace(replace(coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository, '/', '-'), '.', '-'))]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "9456138761658214484" + }, + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", + "metadata": { + "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "cacheRule": { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Cache Rule." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Cache Rule." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Cache Rule." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_webhooks": { + "copy": { + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" + }, + "action": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action'), createArray('chart_delete', 'chart_push', 'delete', 'push', 'quarantine'))]" + }, + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" + }, + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" + }, + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" + }, + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "708950816969162319" + }, + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the webhook." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { + "type": "string", + "metadata": { + "description": "The status of the webhook." + }, + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('acrDnsZoneDeployment-{0}', uniqueString(resourceGroup().id)))]", + "[resourceId('Microsoft.Resources/deployments', parameters('containerRegistryUserAssignedIdentityName'))]" + ] + } + ], + "outputs": { + "containerRegistryId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container registry." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistry-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.resourceId.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name of the container registry." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistry-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.name.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The name of the container registry login server." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistry-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.loginServer.value]" + }, + "containerRegistryUserAssignedIdentityId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned managed identity for the container registry to be able to pull images from it." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('containerRegistryUserAssignedIdentityName')), '2022-09-01').outputs.resourceId.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "Azure Container Registry, where all workload images should be pulled from." + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('keyVault-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.keyVault]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "spokeVNetId": { + "value": "[parameters('spokeVNetId')]" + }, + "hubVNetId": { + "value": "[parameters('hubVNetId')]" + }, + "spokePrivateEndpointSubnetName": { + "value": "[parameters('spokePrivateEndpointSubnetName')]" + }, + "keyVaultPrivateEndpointName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.keyVaultPep]" + }, + "diagnosticWorkspaceId": { + "value": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "16272877246531333695" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created." + } + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the Key Vault." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "hubVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Hub Virtual Network." + } + }, + "spokeVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the VNet to which the private endpoint will be connected." + } + }, + "spokePrivateEndpointSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the subnet in the VNet to which the private endpoint will be connected." + } + }, + "keyVaultPrivateEndpointName": { + "type": "string", + "defaultValue": "keyvault-pep", + "metadata": { + "description": "Optional. The name of the private endpoint to be created for Key Vault. If left empty, it defaults to \"-pep" + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "diagnosticSettingsName": { + "type": "string", + "defaultValue": "keyvault-diagnosticSettings", + "metadata": { + "description": "Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to \"-diagnosticSettings\"." + } + } + }, + "variables": { + "vaultDnsZoneName": "privatelink.vaultcore.azure.net", + "spokeVNetIdTokens": "[split(parameters('spokeVNetId'), '/')]", + "spokeSubscriptionId": "[variables('spokeVNetIdTokens')[2]]", + "spokeResourceGroupName": "[variables('spokeVNetIdTokens')[4]]", + "spokeVNetName": "[variables('spokeVNetIdTokens')[8]]", + "virtualNetworkLinks": "[concat(createArray(createObject('virtualNetworkResourceId', parameters('spokeVNetId'), 'registrationEnabled', false())), if(not(empty(parameters('hubVNetId'))), createArray(createObject('virtualNetworkResourceId', parameters('hubVNetId'), 'registrationEnabled', false())), createArray()))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('keyvaultDnsZoneDeployment-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('vaultDnsZoneName')]" + }, + "location": { + "value": "global" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "virtualNetworkLinks": { + "value": "[variables('virtualNetworkLinks')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "11412633288570781407" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.3.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8657463061873174631" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1725749611610769138" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "12750497126800708872" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "13512848802819943036" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "5069219418696231980" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8649693365938652763" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1580431299497536987" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1628766837655887758" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8423159394395901832" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "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', '2020-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]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('vault-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('keyVaultName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "sku": { + "value": "standard" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny" + } + }, + "enableSoftDelete": { + "value": true + }, + "softDeleteRetentionInDays": { + "value": 7 + }, + "enablePurgeProtection": { + "value": null + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "enableRbacAuthorization": { + "value": true + }, + "enableVaultForDeployment": { + "value": true + }, + "privateEndpoints": { + "value": [ + { + "name": "[parameters('keyVaultPrivateEndpointName')]", + "privateDnsZoneResourceIds": [ + "[reference(resourceId('Microsoft.Resources/deployments', format('keyvaultDnsZoneDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.resourceId.value]" + ], + "subnetResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('spokeSubscriptionId'), variables('spokeResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', variables('spokeVNetName'), parameters('spokePrivateEndpointSubnetName'))]" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "name": "[parameters('diagnosticSettingsName')]", + "workspaceResourceId": "[parameters('diagnosticWorkspaceId')]", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ] + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "4424685997744462747" + }, + "name": "Key Vaults", + "description": "This module deploys a Key Vault.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "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 + }, + "accessPoliciesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + } + }, + "objectId": { + "type": "string", + "metadata": { + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." + } + }, + "applicationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Application ID of the client making request on behalf of a principal." + } + }, + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to keys." + } + }, + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to secrets." + } + }, + "certificates": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to certificates." + } + }, + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to storage accounts." + } + } + }, + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." + } + } + } + }, + "nullable": true + }, + "secretsType": { + "type": "array", + "items": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + } + }, + "nullable": true + }, + "keysType": { + "type": "array", + "items": { + "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/rotationPoliciesType", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + } + }, + "nullable": true + }, + "rotationPoliciesType": { + "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." + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/accessPoliciesType", + "metadata": { + "description": "Optional. All access policies to create." + } + }, + "secrets": { + "$ref": "#/definitions/secretsType", + "nullable": true, + "metadata": { + "description": "Optional. All secrets to create." + } + }, + "keys": { + "$ref": "#/definitions/keysType", + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "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": "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)]" + } + } + ], + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.keyvault-vault.{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" + } + } + } + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.27.1.19265", + "templateHash": "13379419349517171769" + }, + "name": "Key Vault Access Policies", + "description": "This module deploys a Key Vault Access Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "accessPoliciesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + } + }, + "objectId": { + "type": "string", + "metadata": { + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." + } + }, + "applicationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Application ID of the client making request on behalf of a principal." + } + }, + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to keys." + } + }, + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to secrets." + } + }, + "certificates": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to certificates." + } + }, + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to storage accounts." + } + } + }, + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "accessPolicies": { + "$ref": "#/definitions/accessPoliciesType", + "metadata": { + "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", + "input": { + "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", + "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]", + "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" + } + } + ] + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "policies": { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "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(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesEnabled')]" + }, + "attributesExp": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesExp')]" + }, + "attributesNbf": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesNbf')]" + }, + "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.27.1.19265", + "templateHash": "5636875941217257115" + }, + "name": "Key Vault Secrets", + "description": "This module deploys a Key Vault Secret.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "secret_roleAssignments": { + "copy": { + "name": "secret_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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'))]" + }, + "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(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesEnabled')]" + }, + "attributesExp": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesExp')]" + }, + "attributesNbf": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesNbf')]" + }, + "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.27.1.19265", + "templateHash": "2556304156587722941" + }, + "name": "Key Vault Keys", + "description": "This module deploys a Key Vault Key.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "rotationPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy properties object." + } + } + }, + "variables": { + "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 (Preview)": "[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": { + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[parameters('attributesExp')]", + "nbf": "[parameters('attributesNbf')]" + }, + "curveName": "[parameters('curveName')]", + "keyOps": "[parameters('keyOps')]", + "keySize": "[parameters('keySize')]", + "kty": "[parameters('kty')]", + "rotationPolicy": "[coalesce(parameters('rotationPolicy'), createObject())]", + "release_policy": "[coalesce(parameters('releasePolicy'), createObject())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "key_roleAssignments": { + "copy": { + "name": "key_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "key" + ] + } + }, + "outputs": { + "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())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], 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]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('keyvaultDnsZoneDeployment-{0}', uniqueString(resourceGroup().id)))]" + ] + } + ], + "outputs": { + "keyVaultId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vault-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.resourceId.value]" + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the key vault." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vault-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('supportingServicesNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "Azure Key Vault used to hold items like TLS certs and application secrets that your workload will need." + } + } + ], + "outputs": { + "containerRegistryId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Container Registry." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistryModule-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.containerRegistryId.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Container Registry." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistryModule-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.containerRegistryName.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The name of the container registry login server." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistryModule-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "containerRegistryUserAssignedIdentityId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user-assigned managed identity for the Azure Container Registry to be able to pull images from it." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerRegistryModule-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.containerRegistryUserAssignedIdentityId.value]" + }, + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Key Vault." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('keyVault-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.keyVaultId.value]" + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Key Vault." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('keyVault-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.keyVaultName.value]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "hubVNetId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "spokeVNetName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeVNetName.value]" + }, + "spokeInfraSubnetName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeInfraSubnetName.value]" + }, + "enableApplicationInsights": { + "value": "[parameters('enableApplicationInsights')]" + }, + "enableDaprInstrumentation": { + "value": "[parameters('enableDaprInstrumentation')]" + }, + "containerRegistryUserAssignedIdentityId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryUserAssignedIdentityId.value]" + }, + "logAnalyticsWorkspaceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.logAnalyticsWorkspaceId.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.29.47.4906", + "templateHash": "17883690966843525174" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created. This needs to be the same region as the spoke." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "hubVNetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing hub virtual network." + } + }, + "spokeVNetName": { + "type": "string", + "metadata": { + "description": "The name of the existing spoke virtual network." + } + }, + "spokeInfraSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the existing spoke infrastructure subnet." + } + }, + "enableApplicationInsights": { + "type": "bool", + "metadata": { + "description": "Enable or disable the createion of Application Insights." + } + }, + "enableDaprInstrumentation": { + "type": "bool", + "metadata": { + "description": "Enable or disable Dapr application instrumentation using Application Insights. If enableApplicationInsights is false, this parameter is ignored." + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "The resource id of an existing Azure Log Analytics Workspace." + } + }, + "deployZoneRedundantResources": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false." + } + }, + "containerRegistryUserAssignedIdentityId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user-assigned managed identity for the Azure Container Registry to be able to pull images from it." + } + } + }, + "variables": { + "workProfileName": "general-purpose", + "virtualNetworkLinks": "[concat(createArray(createObject('virtualNetworkResourceId', resourceId('Microsoft.Network/virtualNetworks', parameters('spokeVNetName')), 'registrationEnabled', false())), if(not(empty(parameters('hubVNetId'))), createArray(createObject('virtualNetworkResourceId', parameters('hubVNetId'), 'registrationEnabled', false())), createArray()))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('acaNamingDeployment-{0}', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(resourceGroup().id)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + }, + "metadata": { + "description": "User-configured naming rules" + } + }, + { + "condition": "[parameters('enableApplicationInsights')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('applicationInsights-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationInsights]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "workspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "15992072010754675807" + }, + "name": "Application Insights", + "description": "This component deploys an Application Insights instance.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": "", + "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." + } + }, + "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." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.insights-component.{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" + } + } + } + } + }, + "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')]" + } + }, + "appInsights_roleAssignments": { + "copy": { + "name": "appInsights_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Insights/components', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "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": "[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.28.1.47646", + "templateHash": "11213930862679606021" + }, + "name": "Application Insights Linked Storage Account", + "description": "This component deploys an Application Insights Linked Storage Account.", + "owner": "Azure/module-maintainers" + }, + "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": [ + "[resourceId('Microsoft.Resources/deployments', take(format('acaNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "Azure Application Insights, the workload' log & metric sink and APM tool" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceId')]" + }, + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.containerAppsEnvironment]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "internal": { + "value": true + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[parameters('containerRegistryUserAssignedIdentityId')]" + ] + } + }, + "daprAIInstrumentationKey": "[if(and(parameters('enableDaprInstrumentation'), parameters('enableApplicationInsights')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', take(format('applicationInsights-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.instrumentationKey.value), createObject('value', null()))]", + "infrastructureSubnetId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('spokeVNetName'), parameters('spokeInfraSubnetName'))]" + }, + "workloadProfiles": { + "value": [ + { + "maximumCount": 3, + "minimumCount": 0, + "name": "[variables('workProfileName')]", + "workloadProfileType": "D4" + } + ] + }, + "zoneRedundant": { + "value": "[parameters('deployZoneRedundantResources')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "13001855178530816021" + }, + "name": "App ManagedEnvironments", + "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container Apps Managed Environment." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990)." + } + }, + "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/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "logsDestination": { + "type": "string", + "defaultValue": "log-analytics", + "metadata": { + "description": "Optional. Logs destination." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "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." + } + }, + "infrastructureSubnetId": { + "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." + } + }, + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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." + } + } + }, + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.app-managedenvironment.{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" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", + "name": "[last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))]" + }, + "managedEnvironment": { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-11-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "appLogsConfiguration": { + "destination": "[parameters('logsDestination')]", + "logAnalyticsConfiguration": { + "customerId": "[reference('logAnalyticsWorkspace').customerId]", + "sharedKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))), '2021-06-01').primarySharedKey]" + } + }, + "daprAIConnectionString": "[parameters('daprAIConnectionString')]", + "daprAIInstrumentationKey": "[parameters('daprAIInstrumentationKey')]", + "customDomainConfiguration": { + "certificatePassword": "[parameters('certificatePassword')]", + "certificateValue": "[if(not(empty(parameters('certificateValue'))), parameters('certificateValue'), null())]", + "dnsSuffix": "[parameters('dnsSuffix')]" + }, + "vnetConfiguration": { + "internal": "[parameters('internal')]", + "infrastructureSubnetId": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('infrastructureSubnetId'), null())]", + "dockerBridgeCidr": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('dockerBridgeCidr'), null())]", + "platformReservedCidr": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedCidr'), null())]", + "platformReservedDnsIP": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedDnsIP'), null())]" + }, + "workloadProfiles": "[if(not(empty(parameters('workloadProfiles'))), parameters('workloadProfiles'), null())]", + "zoneRedundant": "[parameters('zoneRedundant')]", + "infrastructureResourceGroup": "[parameters('infrastructureResourceGroupName')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "managedEnvironment_roleAssignments": { + "copy": { + "name": "managedEnvironment_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/managedEnvironments/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.App/managedEnvironments', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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" + ] + } + }, + "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', '2023-11-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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2023-11-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]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('applicationInsights-{0}', uniqueString(resourceGroup().id)), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('acaNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "The Azure Container Apps (ACA) cluster." + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('acaDnsZoneDeployment-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.defaultDomain.value]" + }, + "location": { + "value": "global" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "virtualNetworkLinks": { + "value": "[variables('virtualNetworkLinks')]" + }, + "a": { + "value": [ + { + "name": "*", + "aRecords": [ + { + "ipv4Address": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.staticIp.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.28.1.47646", + "templateHash": "11412633288570781407" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.3.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8657463061873174631" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1725749611610769138" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "12750497126800708872" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "13512848802819943036" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "5069219418696231980" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8649693365938652763" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1580431299497536987" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1628766837655887758" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8423159394395901832" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "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', '2020-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": [ + "[resourceId('Microsoft.Resources/deployments', take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64))]" + ], + "metadata": { + "description": "The Private DNS zone containing the ACA load balancer IP" + } + } + ], + "outputs": { + "containerAppsEnvironmentId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Container Apps environment." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The name of the Container Apps environment." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('acaenv-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.name.value]" + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the Application Insights instance." + }, + "value": "[if(parameters('enableApplicationInsights'), reference(resourceId('Microsoft.Resources/deployments', take(format('applicationInsights-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.name.value, '')]" + }, + "workloadProfileNames": { + "type": "array", + "metadata": { + "description": "The name of the workload profiles provisioned in the Container Apps environment." + }, + "value": [ + "[variables('workProfileName')]" + ] + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64))]" + ] + }, + { + "condition": "[parameters('deploySampleApplication')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('sampleApplication-{0}-deployment', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "containerAppsEnvironmentId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerAppsEnvironmentId.value]" + }, + "workloadProfileName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.workloadProfileNames.value[0]]" + }, + "containerRegistryUserAssignedIdentityId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryUserAssignedIdentityId.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.29.47.4906", + "templateHash": "16668830347449265665" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created. This needs to be the same region as the Azure Container Apps instances." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "helloWorldContainerAppName": { + "type": "string", + "defaultValue": "ca-simple-hello", + "minLength": 2, + "maxLength": 32, + "metadata": { + "description": "Optional. The name of the Container App. If set, it overrides the name generated by the template." + } + }, + "containerRegistryUserAssignedIdentityId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing user-assigned managed identity to be assigned to the Container App to be able to pull images from the container registry." + } + }, + "containerAppsEnvironmentId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing Container Apps environment in which the Container App will be deployed." + } + }, + "workloadProfileName": { + "type": "string", + "metadata": { + "description": "The container apps environment workload profile to use for the Container App." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('helloWorldContainerAppName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('helloWorldContainerAppName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "environmentId": { + "value": "[parameters('containerAppsEnvironmentId')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[parameters('containerRegistryUserAssignedIdentityId')]" + ] + } + }, + "workloadProfileName": { + "value": "[parameters('workloadProfileName')]" + }, + "containers": { + "value": [ + { + "name": "simple-hello", + "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", + "resources": { + "cpu": "[json('0.25')]", + "memory": "0.5Gi" + } + } + ] + }, + "scaleMinReplicas": { + "value": 2 + }, + "scaleMaxReplicas": { + "value": 10 + }, + "activeRevisionsMode": { + "value": "Single" + }, + "ingressExternal": { + "value": true + }, + "ingressAllowInsecure": { + "value": false + }, + "ingressTargetPort": { + "value": 80 + }, + "ingressTransport": { + "value": "auto" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "6251669835322227304" + }, + "name": "Container Apps", + "description": "This module deploys a Container App.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "container": { + "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/environmentVar" + }, + "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/containerAppProbe" + }, + "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/volumeMount" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container volume mounts." + } + } + } + }, + "environmentVar": { + "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." + } + } + } + }, + "containerAppProbe": { + "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/containerAppProbeHttpGet", + "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/containerAppProbeTcpSocket", + "nullable": true, + "metadata": { + "description": "Optional. TCPSocket 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." + } + } + } + }, + "containerAppProbeHttpGet": { + "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/containerAppProbeHttpGetHeadersItem" + }, + "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." + } + } + } + }, + "containerAppProbeHttpGetHeadersItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the header." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. Value of the header." + } + } + } + }, + "containerAppProbeTcpSocket": { + "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." + } + } + } + }, + "volumeMount": { + "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." + } + } + } + } + }, + "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." + } + }, + "ingressExternal": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Bool indicating if the App exposes an external HTTP endpoint." + } + }, + "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." + } + }, + "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." + } + }, + "scaleMaxReplicas": { + "type": "int", + "defaultValue": 10, + "metadata": { + "description": "Optional. Maximum number of container replicas. Defaults to 10 if not set." + } + }, + "scaleMinReplicas": { + "type": "int", + "defaultValue": 3, + "metadata": { + "description": "Optional. Minimum number of container replicas. Defaults to 3 if not set." + } + }, + "scaleRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Scaling rules." + } + }, + "activeRevisionsMode": { + "type": "string", + "defaultValue": "Single", + "allowedValues": [ + "Multiple", + "Single" + ], + "metadata": { + "description": "Optional. Controls how active revisions are handled for the Container app." + } + }, + "environmentId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of environment." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registries": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Collection of private container registry credentials for containers used by the Container app." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom domain bindings for Container App hostnames." + } + }, + "exposedPort": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Exposed Port in containers for TCP traffic from ingress." + } + }, + "ipSecurityRestrictions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Rules to restrict incoming IP address." + } + }, + "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", + "defaultValue": {}, + "metadata": { + "description": "Optional. Dapr configuration for the Container App." + } + }, + "maxInactiveRevisions": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Max inactive revisions a Container App can have." + } + }, + "containers": { + "type": "array", + "items": { + "$ref": "#/definitions/container" + }, + "metadata": { + "description": "Required. List of container definitions for the Container App." + } + }, + "initContainersTemplate": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of specialized containers that run before app containers." + } + }, + "secrets": { + "type": "secureObject", + "defaultValue": {}, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. List of volume definitions for the Container App." + } + }, + "workloadProfileName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Workload profile name to pin for container app execution." + } + } + }, + "variables": { + "secretList": "[if(not(empty(parameters('secrets'))), parameters('secrets').secureList, createArray())]", + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.app-containerapp.{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" + } + } + } + } + }, + "containerApp": { + "type": "Microsoft.App/containerApps", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "properties": { + "environmentId": "[parameters('environmentId')]", + "configuration": { + "activeRevisionsMode": "[parameters('activeRevisionsMode')]", + "dapr": "[if(not(empty(parameters('dapr'))), parameters('dapr'), null())]", + "ingress": { + "allowInsecure": "[parameters('ingressAllowInsecure')]", + "customDomains": "[if(not(empty(parameters('customDomains'))), parameters('customDomains'), null())]", + "exposedPort": "[parameters('exposedPort')]", + "external": "[parameters('ingressExternal')]", + "ipSecurityRestrictions": "[if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null())]", + "targetPort": "[parameters('ingressTargetPort')]", + "stickySessions": { + "affinity": "[parameters('stickySessionsAffinity')]" + }, + "traffic": [ + { + "label": "[parameters('trafficLabel')]", + "latestRevision": "[parameters('trafficLatestRevision')]", + "revisionName": "[parameters('trafficRevisionName')]", + "weight": "[parameters('trafficWeight')]" + } + ], + "transport": "[parameters('ingressTransport')]" + }, + "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]", + "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]", + "secrets": "[variables('secretList')]" + }, + "template": { + "containers": "[parameters('containers')]", + "initContainers": "[if(not(empty(parameters('initContainersTemplate'))), parameters('initContainersTemplate'), null())]", + "revisionSuffix": "[parameters('revisionSuffix')]", + "scale": { + "maxReplicas": "[parameters('scaleMaxReplicas')]", + "minReplicas": "[parameters('scaleMinReplicas')]", + "rules": "[if(not(empty(parameters('scaleRules'))), parameters('scaleRules'), null())]" + }, + "volumes": "[if(not(empty(parameters('volumes'))), parameters('volumes'), null())]" + }, + "workloadProfileName": "[parameters('workloadProfileName')]" + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/containerApps/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.App/containerApps', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "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": "[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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('containerApp', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('containerApp', '2023-05-01', 'full').location]" + } + } + } + } + } + ], + "outputs": { + "helloWorldAppFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the \"Hello World\" Container App." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('helloWorldContainerAppName')), '2022-09-01').outputs.fqdn.value]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64))]" + ] + }, + { + "condition": "[equals(parameters('exposeContainerAppsWith'), 'applicationGateway')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('applicationGateway-{0}-deployment', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "applicationGatewayCertificateKeyName": { + "value": "[parameters('applicationGatewayCertificateKeyName')]" + }, + "applicationGatewayFqdn": { + "value": "[parameters('applicationGatewayFqdn')]" + }, + "applicationGatewayPrimaryBackendEndFqdn": "[if(parameters('deploySampleApplication'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('sampleApplication-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.helloWorldAppFqdn.value), createObject('value', ''))]", + "applicationGatewaySubnetId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeApplicationGatewaySubnetId.value]" + }, + "base64Certificate": { + "value": "[parameters('base64Certificate')]" + }, + "keyVaultId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.keyVaultResourceId.value]" + }, + "deployZoneRedundantResources": { + "value": "[parameters('deployZoneRedundantResources')]" + }, + "enableDdosProtection": { + "value": "[parameters('enableDdosProtection')]" + }, + "applicationGatewayLogAnalyticsId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.logAnalyticsWorkspaceId.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.29.47.4906", + "templateHash": "13769306330739717588" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created. This needs to be the same region as the spoke." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Defines whether to enable telemetry for the modules deployment." + } + }, + "applicationGatewayFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the Application Gateawy. Must match the TLS certificate." + } + }, + "applicationGatewaySubnetId": { + "type": "string", + "metadata": { + "description": "The existing subnet resource ID to use for Application Gateway." + } + }, + "applicationGatewayPrimaryBackendEndFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the primary backend endpoint." + } + }, + "appGatewayBackendHealthProbePath": { + "type": "string", + "defaultValue": "/", + "metadata": { + "description": "The path to use for Application Gateway's backend health probe." + } + }, + "base64Certificate": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault." + } + }, + "applicationGatewayCertificateKeyName": { + "type": "string", + "metadata": { + "description": "The name of the certificate key to use for Application Gateway certificate." + } + }, + "applicationGatewayLogAnalyticsId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource ID of the exsiting Log Analytics workload for diagnostic settngs, or nothing if you don't need any." + } + }, + "keyVaultId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing Key Vault which contains Application Gateway's cert." + } + }, + "deployZoneRedundantResources": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false." + } + }, + "enableDdosProtection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. DDoS protection mode for the Public IP of the Application Gateway. See https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus" + } + } + }, + "variables": { + "keyVaultIdTokens": "[split(parameters('keyVaultId'), '/')]", + "keyVaultSubscriptionId": "[variables('keyVaultIdTokens')[2]]", + "keyVaultResourceGroupName": "[variables('keyVaultIdTokens')[4]]", + "keyVaultName": "[variables('keyVaultIdTokens')[8]]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('agwNamingDeployment-{0}', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(resourceGroup().id)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + }, + "metadata": { + "description": "User-configured naming rules" + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('appGwUserAssignedIdentity-Deployment-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGatewayUserAssignedIdentity]" + }, + "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.26.54.24096", + "templateHash": "998408512764899332" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity.", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "federatedIdentityCredentialsType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/federatedIdentityCredentialsType", + "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", + "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 of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.2.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": "2023-01-31", + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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-FederatedIdentityCredential-{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.26.54.24096", + "templateHash": "4317497001099502136" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential.", + "owner": "Azure/module-maintainers" + }, + "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": "2023-01-31", + "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', '2023-01-31', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64))]" + ], + "metadata": { + "description": "A user-assigned managed identity that enables Application Gateway to access Key Vault for its TLS certs." + } + }, + { + "condition": "[not(empty(parameters('base64Certificate')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('appGatewayAddCertificates-Deployment-{0}', uniqueString(resourceGroup().id)), 64)]", + "subscriptionId": "[variables('keyVaultSubscriptionId')]", + "resourceGroup": "[variables('keyVaultResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[variables('keyVaultName')]" + }, + "appGatewayCertificateData": { + "value": "[parameters('base64Certificate')]" + }, + "appGatewayCertificateKeyName": { + "value": "[parameters('applicationGatewayCertificateKeyName')]" + }, + "appGatewayUserAssignedIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('appGwUserAssignedIdentity-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.principalId.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.29.47.4906", + "templateHash": "15933024016056946085" + } + }, + "parameters": { + "keyVaultName": { + "type": "string" + }, + "appGatewayUserAssignedIdentityPrincipalId": { + "type": "string" + }, + "appGatewayCertificateKeyName": { + "type": "string" + }, + "appGatewayCertificateData": { + "type": "string" + } + }, + "variables": { + "keyVaultSecretUserRoleGuid": "4633458b-17de-408a-b874-0445c86b69e6" + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('appGatewayCertificateKeyName'))]", + "properties": { + "value": "[parameters('appGatewayCertificateData')]", + "contentType": "application/x-pkcs12", + "attributes": { + "enabled": true + } + } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('appGatewayCertificateKeyName'))]", + "name": "[guid(subscription().id, resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), parameters('appGatewayUserAssignedIdentityPrincipalId'), 'KeyVaultSecretUser')]", + "properties": { + "principalId": "[parameters('appGatewayUserAssignedIdentityPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('keyVaultSecretUserRoleGuid'))]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('appGatewayCertificateKeyName'))]" + ] + } + ], + "outputs": { + "SecretUri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('appGatewayCertificateKeyName')), '2023-07-01').secretUri]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('appGwUserAssignedIdentity-Deployment-{0}', uniqueString(resourceGroup().id)), 64))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('applicationGatewayPublicIp-Deployment-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGatewayPip]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "skuName": { + "value": "Standard" + }, + "publicIPAllocationMethod": { + "value": "Static" + }, + "ddosSettings": "[if(parameters('enableDdosProtection'), createObject('value', createObject('protectionMode', 'Enabled')), createObject('value', null()))]", + "zones": "[if(parameters('deployZoneRedundantResources'), createObject('value', createArray(1, 2, 3)), createObject('value', createArray()))]", + "diagnosticSettings": { + "value": [ + { + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "publicIpDiagnosticSettings", + "workspaceResourceId": "[parameters('applicationGatewayLogAnalyticsId')]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "10356333973104369631" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "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." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{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" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-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": null + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('applicationGateway-Deployment-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "backendAddressPools": { + "value": [ + { + "name": "acaServiceBackend", + "properties": { + "backendAddresses": "[if(not(empty(parameters('applicationGatewayPrimaryBackendEndFqdn'))), createArray(createObject('fqdn', parameters('applicationGatewayPrimaryBackendEndFqdn'))), null())]" + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "https", + "properties": { + "port": 443, + "protocol": "Https", + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "requestTimeout": 20, + "probe": "[if(not(empty(parameters('applicationGatewayPrimaryBackendEndFqdn'))), createObject('id', resourceId('Microsoft.Network/applicationGateways/probes', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway, 'webProbe')), null())]" + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "workspaceResourceId": "[parameters('applicationGatewayLogAnalyticsId')]" + } + ] + }, + "enableHttp2": { + "value": true + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "appGwPublicFrontendIp", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[reference(resourceId('Microsoft.Resources/deployments', take(format('applicationGatewayPublicIp-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.resourceId.value]" + } + } + } + ] + }, + "frontendPorts": "[if(not(empty(parameters('base64Certificate'))), createObject('value', createArray(createObject('name', 'port_443', 'properties', createObject('port', 443)), createObject('name', 'port_80', 'properties', createObject('port', 80)))), createObject('value', createArray(createObject('name', 'port_80', 'properties', createObject('port', 80)))))]", + "gatewayIPConfigurations": { + "value": [ + { + "name": "appGatewayIpConfig", + "properties": { + "subnet": { + "id": "[parameters('applicationGatewaySubnetId')]" + } + } + } + ] + }, + "httpListeners": "[if(empty(parameters('base64Certificate')), createObject('value', createArray(createObject('name', 'httpListener', 'properties', createObject('frontendIPConfiguration', createObject('id', format('{0}/frontendIPConfigurations/appGwPublicFrontendIp', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))), 'frontendPort', createObject('id', format('{0}/frontendPorts/port_80', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))), 'protocol', 'Http', 'hostnames', createArray(), 'requireServerNameIndication', false())))), createObject('value', createArray(createObject('name', 'httpsListener', 'properties', createObject('frontendIPConfiguration', createObject('id', format('{0}/frontendIPConfigurations/appGwPublicFrontendIp', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))), 'frontendPort', createObject('id', format('{0}/frontendPorts/port_443', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))), 'protocol', 'Https', 'sslCertificate', createObject('id', format('{0}/sslCertificates/{1}', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway), parameters('applicationGatewayFqdn'))), 'hostnames', createArray(), 'requireServerNameIndication', false())))))]", + "location": { + "value": "[parameters('location')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[reference(resourceId('Microsoft.Resources/deployments', take(format('appGwUserAssignedIdentity-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.resourceId.value]" + ] + } + }, + "probes": "[if(not(empty(parameters('applicationGatewayPrimaryBackendEndFqdn'))), createObject('value', createArray(createObject('name', 'webProbe', 'properties', createObject('protocol', 'Https', 'host', parameters('applicationGatewayPrimaryBackendEndFqdn'), 'path', parameters('appGatewayBackendHealthProbePath'), 'interval', 30, 'timeout', 30, 'unhealthyThreshold', 3, 'pickHostNameFromBackendHttpSettings', false(), 'minServers', 0, 'match', createObject('statusCodes', createArray('200-499')))))), createObject('value', null()))]", + "requestRoutingRules": { + "value": [ + { + "name": "routingRules", + "properties": { + "ruleType": "Basic", + "priority": 100, + "httpListener": { + "id": "[format('{0}/httpListeners/httpListener', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))]" + }, + "backendAddressPool": { + "id": "[format('{0}/backendAddressPools/acaServiceBackend', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))]" + }, + "backendHttpSettings": { + "id": "[format('{0}/backendHttpSettingsCollection/https', resourceId('Microsoft.Network/applicationGateways', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway))]" + } + } + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": "[if(not(empty(parameters('base64Certificate'))), createObject('value', createArray(createObject('name', parameters('applicationGatewayFqdn'), 'properties', createObject('keyVaultSecretId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('keyVaultSubscriptionId'), variables('keyVaultResourceGroupName')), 'Microsoft.Resources/deployments', take(format('appGatewayAddCertificates-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.SecretUri.value)))), createObject('value', createArray()))]", + "tags": { + "value": "[parameters('tags')]" + }, + "firewallPolicyId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('appGwWafPolicy-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "sslPolicyType": { + "value": "Predefined" + }, + "sslPolicyName": { + "value": "AppGwSslPolicy20220101" + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "8087239342888324125" + }, + "name": "Network Application Gateways", + "description": "This module deploys a Network Application Gateway.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the 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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 80, + "metadata": { + "description": "Required. Name of the Application Gateway." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "authenticationCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Authentication certificates of the application gateway resource." + } + }, + "autoscaleMaxCapacity": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Upper bound on number of Application Gateway capacity." + } + }, + "autoscaleMinCapacity": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Lower bound on number of Application Gateway capacity." + } + }, + "backendAddressPools": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend address pool of the application gateway resource." + } + }, + "backendHttpSettingsCollection": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend http settings of the application gateway resource." + } + }, + "customErrorConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom error configurations of the application gateway resource." + } + }, + "enableFips": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether FIPS is enabled on the application gateway resource." + } + }, + "enableHttp2": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether HTTP2 is enabled on the application gateway resource." + } + }, + "firewallPolicyId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of an associated firewall policy. Should be configured for security reasons." + } + }, + "frontendIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Frontend IP addresses of the application gateway resource." + } + }, + "frontendPorts": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Frontend ports of the application gateway resource." + } + }, + "gatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Subnets of the application gateway resource." + } + }, + "enableRequestBuffering": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable request buffering." + } + }, + "enableResponseBuffering": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable response buffering." + } + }, + "httpListeners": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Http listeners of the application gateway resource." + } + }, + "loadDistributionPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Load distribution policies of the application gateway resource." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "privateLinkConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. PrivateLink configurations on application gateway." + } + }, + "probes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Probes of the application gateway resource." + } + }, + "redirectConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Redirect configurations of the application gateway resource." + } + }, + "requestRoutingRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Request routing rules of the application gateway resource." + } + }, + "rewriteRuleSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Rewrite rules for the application gateway resource." + } + }, + "sku": { + "type": "string", + "defaultValue": "WAF_v2", + "allowedValues": [ + "Standard_Small", + "Standard_Medium", + "Standard_Large", + "WAF_Medium", + "WAF_Large", + "Standard_v2", + "WAF_v2" + ], + "metadata": { + "description": "Optional. The name of the SKU for the Application Gateway." + } + }, + "capacity": { + "type": "int", + "defaultValue": 2, + "minValue": 0, + "maxValue": 10, + "metadata": { + "description": "Optional. The number of Application instances to be configured." + } + }, + "sslCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SSL certificates of the application gateway resource." + } + }, + "sslPolicyCipherSuites": { + "type": "array", + "defaultValue": [ + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + ], + "allowedValues": [ + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384" + ], + "metadata": { + "description": "Optional. Ssl cipher suites to be enabled in the specified order to application gateway." + } + }, + "sslPolicyMinProtocolVersion": { + "type": "string", + "defaultValue": "TLSv1_2", + "allowedValues": [ + "TLSv1_0", + "TLSv1_1", + "TLSv1_2", + "TLSv1_3" + ], + "metadata": { + "description": "Optional. Ssl protocol enums." + } + }, + "sslPolicyName": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "AppGwSslPolicy20150501", + "AppGwSslPolicy20170401", + "AppGwSslPolicy20170401S", + "AppGwSslPolicy20220101", + "AppGwSslPolicy20220101S", + "" + ], + "metadata": { + "description": "Optional. Ssl predefined policy name enums." + } + }, + "sslPolicyType": { + "type": "string", + "defaultValue": "Custom", + "allowedValues": [ + "Custom", + "CustomV2", + "Predefined" + ], + "metadata": { + "description": "Optional. Type of Ssl Policy." + } + }, + "sslProfiles": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SSL profiles of the application gateway resource." + } + }, + "trustedClientCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Trusted client certificates of the application gateway resource." + } + }, + "trustedRootCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Trusted Root certificates of the application gateway resource." + } + }, + "urlPathMaps": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. URL path map of the application gateway resource." + } + }, + "webApplicationFirewallConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Application gateway web application firewall configuration. Should be configured for security reasons." + } + }, + "zones": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of availability zones denoting where the resource needs to come from." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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. Resource tags." + } + }, + "backendSettingsCollection": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend settings of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits)." + } + }, + "listeners": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Listeners of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits)." + } + }, + "routingRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Routing rules of the application gateway resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "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(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')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-appgw.{0}.{1}', replace('0.1.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" + } + } + } + } + }, + "applicationGateway": { + "type": "Microsoft.Network/applicationGateways", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": "[union(createObject('authenticationCertificates', parameters('authenticationCertificates'), 'autoscaleConfiguration', if(and(greater(parameters('autoscaleMaxCapacity'), 0), greaterOrEquals(parameters('autoscaleMinCapacity'), 0)), createObject('maxCapacity', parameters('autoscaleMaxCapacity'), 'minCapacity', parameters('autoscaleMinCapacity')), null()), 'backendAddressPools', parameters('backendAddressPools'), 'backendHttpSettingsCollection', parameters('backendHttpSettingsCollection'), 'backendSettingsCollection', parameters('backendSettingsCollection'), 'customErrorConfigurations', parameters('customErrorConfigurations'), 'enableHttp2', parameters('enableHttp2'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'forceFirewallPolicyAssociation', not(empty(parameters('firewallPolicyId'))), 'frontendIPConfigurations', parameters('frontendIPConfigurations'), 'frontendPorts', parameters('frontendPorts'), 'gatewayIPConfigurations', parameters('gatewayIPConfigurations'), 'globalConfiguration', if(endsWith(parameters('sku'), 'v2'), createObject('enableRequestBuffering', parameters('enableRequestBuffering'), 'enableResponseBuffering', parameters('enableResponseBuffering')), null()), 'httpListeners', parameters('httpListeners'), 'loadDistributionPolicies', parameters('loadDistributionPolicies'), 'listeners', parameters('listeners'), 'privateLinkConfigurations', parameters('privateLinkConfigurations'), 'probes', parameters('probes'), 'redirectConfigurations', parameters('redirectConfigurations'), 'requestRoutingRules', parameters('requestRoutingRules'), 'routingRules', parameters('routingRules'), 'rewriteRuleSets', parameters('rewriteRuleSets'), 'sku', createObject('name', parameters('sku'), 'tier', if(endsWith(parameters('sku'), 'v2'), parameters('sku'), substring(parameters('sku'), 0, indexOf(parameters('sku'), '_'))), 'capacity', if(and(greater(parameters('autoscaleMaxCapacity'), 0), greaterOrEquals(parameters('autoscaleMinCapacity'), 0)), null(), parameters('capacity'))), 'sslCertificates', parameters('sslCertificates'), 'sslPolicy', if(not(equals(parameters('sslPolicyType'), 'Predefined')), createObject('cipherSuites', parameters('sslPolicyCipherSuites'), 'minProtocolVersion', parameters('sslPolicyMinProtocolVersion'), 'policyName', if(empty(parameters('sslPolicyName')), null(), parameters('sslPolicyName')), 'policyType', parameters('sslPolicyType')), createObject('policyName', if(empty(parameters('sslPolicyName')), null(), parameters('sslPolicyName')), 'policyType', parameters('sslPolicyType'))), 'sslProfiles', parameters('sslProfiles'), 'trustedClientCertificates', parameters('trustedClientCertificates'), 'trustedRootCertificates', parameters('trustedRootCertificates'), 'urlPathMaps', parameters('urlPathMaps')), if(parameters('enableFips'), createObject('enableFips', parameters('enableFips')), createObject()), if(not(empty(parameters('webApplicationFirewallConfiguration'))), createObject('webApplicationFirewallConfiguration', parameters('webApplicationFirewallConfiguration')), createObject()))]", + "zones": "[parameters('zones')]" + }, + "applicationGateway_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/applicationGateways/{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": [ + "applicationGateway" + ] + }, + "applicationGateway_diagnosticSettings": { + "copy": { + "name": "applicationGateway_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/applicationGateways/{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": [ + "applicationGateway" + ] + }, + "applicationGateway_roleAssignments": { + "copy": { + "name": "applicationGateway_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/applicationGateways/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/applicationGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "applicationGateway" + ] + }, + "applicationGateway_privateEndpoints": { + "copy": { + "name": "applicationGateway_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-applicationGateway-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Network/applicationGateways', 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.Network/applicationGateways', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Network/applicationGateways', 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.Network/applicationGateways', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Network/applicationGateways', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "applicationGateway" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the application gateway." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application gateway." + }, + "value": "[resourceId('Microsoft.Network/applicationGateways', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the application gateway was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('applicationGateway', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('keyVaultSubscriptionId'), variables('keyVaultResourceGroupName')), 'Microsoft.Resources/deployments', take(format('appGatewayAddCertificates-Deployment-{0}', uniqueString(resourceGroup().id)), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('appGwWafPolicy-Deployment-{0}', uniqueString(resourceGroup().id)), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('applicationGatewayPublicIp-Deployment-{0}', uniqueString(resourceGroup().id)), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64))]", + "[resourceId('Microsoft.Resources/deployments', take(format('appGwUserAssignedIdentity-Deployment-{0}', uniqueString(resourceGroup().id)), 64))]" + ], + "metadata": { + "description": "The Application Gateway." + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('appGwWafPolicy-Deployment-{0}', uniqueString(resourceGroup().id)), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}Policy001', reference(resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.applicationGateway)]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "policySettings": { + "value": { + "fileUploadLimitInMb": 10, + "state": "Enabled", + "mode": "Prevention" + } + }, + "managedRules": { + "value": { + "managedRuleSets": [ + { + "ruleSetType": "OWASP", + "ruleSetVersion": "3.2", + "ruleGroupOverrides": [] + }, + { + "ruleSetType": "Microsoft_BotManagerRuleSet", + "ruleSetVersion": "0.1" + } + ] + } + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "15441807103053057108" + }, + "name": "Application Gateway Web Application Firewall (WAF) Policies", + "description": "This module deploys an Application Gateway Web Application Firewall (WAF) Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Application Gateway WAF policy." + } + }, + "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." + } + }, + "managedRules": { + "type": "object", + "metadata": { + "description": "Required. Describes the managedRules structure." + } + }, + "customRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The custom rules inside the policy." + } + }, + "policySettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The PolicySettings for policy." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-appgwwebappfirewallpolicy.{0}.{1}', replace('0.1.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" + } + } + } + } + }, + "applicationGatewayWAFPolicy": { + "type": "Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies", + "apiVersion": "2022-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "managedRules": "[coalesce(parameters('managedRules'), createObject())]", + "customRules": "[parameters('customRules')]", + "policySettings": "[parameters('policySettings')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the application gateway WAF policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application gateway WAF policy." + }, + "value": "[resourceId('Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the application gateway WAF policy was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('applicationGatewayWAFPolicy', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('agwNamingDeployment-{0}', deployment().name), 64))]" + ] + } + ], + "outputs": { + "applicationGatewayResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Application Gateway." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('applicationGateway-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.resourceId.value]" + }, + "applicationGatewayFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the Azure Application Gateway." + }, + "value": "[parameters('applicationGatewayFqdn')]" + }, + "applicationGatewayPublicIp": { + "type": "string", + "metadata": { + "description": "The public IP address of the Azure Application Gateway." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('applicationGatewayPublicIp-Deployment-{0}', uniqueString(resourceGroup().id)), 64)), '2022-09-01').outputs.ipAddress.value]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('sampleApplication-{0}-deployment', deployment().name), 64))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64))]" + ] + }, + { + "condition": "[equals(parameters('exposeContainerAppsWith'), 'frontDoor')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('frontDoor-{0}-deployment', deployment().name), 64)]", + "resourceGroup": "[variables('rgSpokeName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "containerAppsEnvironmentId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerAppsEnvironmentId.value]" + }, + "frontDoorOriginHostName": "[if(parameters('deploySampleApplication'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('sampleApplication-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.helloWorldAppFqdn.value), createObject('value', ''))]", + "privateLinkSubnetId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeInfraSubnetId.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.29.47.4906", + "templateHash": "2374255941922404406" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workload that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\"). Up to 8 characters long." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Whether to enable deplotment telemetry." + } + }, + "containerAppsEnvironmentId": { + "type": "string", + "metadata": { + "description": "The ID of the Container Apps environment to be used for the deployment. (e.g. /subscriptions/XXX/resourceGroups/XXX/providers/Microsoft.App/managedEnvironments/XXX)" + } + }, + "privateLinkSubnetId": { + "type": "string", + "metadata": { + "description": "The resource ID of the subnet to be used for the private link service. (e.g. /subscriptions/XXX/resourceGroups/XXX/providers/Microsoft.Network/virtualNetworks/XXX/subnets/XXX)" + } + }, + "frontDoorEndpointName": { + "type": "string", + "defaultValue": "fde-containerapps", + "metadata": { + "description": "The name of the front door endpoint to be created." + } + }, + "frontDoorOriginGroupName": { + "type": "string", + "defaultValue": "containerapps-origin-group", + "metadata": { + "description": "The name of the front door origin group to be created." + } + }, + "frontDoorOriginName": { + "type": "string", + "defaultValue": "containerapps-origin", + "metadata": { + "description": "The name of the front door origin to be created." + } + }, + "frontDoorOriginRouteName": { + "type": "string", + "defaultValue": "containerapps-route", + "metadata": { + "description": "The name of the front door origin route to be created." + } + }, + "frontDoorOriginHostName": { + "type": "string", + "metadata": { + "description": "The host name of the front door origin to be created." + } + } + }, + "variables": { + "containerAppsEnvironmentTokens": "[split(parameters('containerAppsEnvironmentId'), '/')]", + "containerAppsEnvironmentSubscriptionId": "[variables('containerAppsEnvironmentTokens')[2]]", + "containerAppsEnvironmentResourceGroupName": "[variables('containerAppsEnvironmentTokens')[4]]", + "containerAppsEnvironmentName": "[variables('containerAppsEnvironmentTokens')[8]]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('frontDoorNamingDeployment-{0}', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "uniqueId": { + "value": "[uniqueString(resourceGroup().id)]" + }, + "environment": { + "value": "[parameters('environment')]" + }, + "workloadName": { + "value": "[parameters('workloadName')]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4794676376542080363" + } + }, + "parameters": { + "workloadName": { + "type": "string", + "minLength": 2, + "maxLength": 10, + "metadata": { + "description": "The name of the workloard that is being deployed. Up to 10 characters long." + } + }, + "environment": { + "type": "string", + "maxLength": 8, + "metadata": { + "description": "The name of the environment (e.g. \"dev\", \"test\", \"prod\", \"uat\", \"dr\", \"qa\") Up to 8 characters long." + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all Resources." + } + }, + "uniqueId": { + "type": "string", + "metadata": { + "description": "a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness" + } + } + }, + "variables": { + "$fxv#0": "{\n // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations\n \"resourceTypeAbbreviations\" : {\n \"applicationGateway\": \"agw\",\n \"applicationInsights\": \"appi\",\n \"appService\": \"app\",\n \"azureFirewall\": \"azfw\",\n \"bastion\": \"bas\",\n \"containerAppsEnvironment\": \"cae\",\n \"containerRegistry\": \"cr\",\n \"cosmosDbNoSql\": \"cosno\",\n \"frontDoor\": \"afd\",\n \"frontDoorEndpoint\": \"fde\",\n \"frontDoorWaf\": \"fdfp\",\n \"keyVault\": \"kv\",\n \"logAnalyticsWorkspace\": \"log\",\n \"managedIdentity\": \"id\",\n \"networkInterface\": \"nic\",\n \"networkSecurityGroup\": \"nsg\",\n \"privateEndpoint\": \"pep\",\n \"privateLinkService\": \"pls\",\n \"publicIpAddress\": \"pip\",\n \"resourceGroup\": \"rg\",\n \"routeTable\": \"rt\",\n \"serviceBus\": \"sb\",\n \"serviceBusQueue\": \"sbq\",\n \"serviceBusTopic\": \"sbt\",\n \"storageAccount\": \"st\",\n \"virtualMachine\": \"vm\",\n \"virtualNetwork\": \"vnet\",\n \"redisCache\": \"redis\",\n \"cognitiveAccount\": \"cog\",\n \"openAiDeployment\": \"oaidep\",\n \"azureAISearch\": \"srch\",\n \"documentIntelligence\": \"di\",\n \"eventGridSystemTopic\": \"egst\",\n \"eventGridSubscription\": \"evgs\"\n },\n\n //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json\n \"regionAbbreviations\" : {\n \"australiacentral\": \"auc\",\n \"australiacentral2\": \"auc2\",\n \"australiaeast\": \"aue\",\n \"australiasoutheast\": \"ause\",\n \"brazilsouth\": \"brs\",\n \"brazilsoutheast\": \"brse\",\n \"canadacentral\": \"canc\",\n \"canadaeast\": \"cane\",\n \"centralindia\": \"cin\",\n \"centralus\": \"cus\",\n \"centraluseuap\": \"cuseuap\",\n \"eastasia\": \"ea\",\n \"eastus\": \"eus\",\n \"eastus2\": \"eus2\",\n \"eastus2euap\": \"eus2euap\",\n \"francecentral\": \"frc\",\n \"francesouth\": \"frs\",\n \"germanynorth\": \"gern\",\n \"germanywestcentral\": \"gerwc\",\n \"japaneast\": \"jae\",\n \"japanwest\": \"jaw\",\n \"jioindiacentral\": \"jioinc\",\n \"jioindiawest\": \"jioinw\",\n \"koreacentral\": \"koc\",\n \"koreasouth\": \"kors\",\n \"northcentralus\": \"ncus\",\n \"northeurope\": \"neu\",\n \"norwayeast\": \"nore\",\n \"norwaywest\": \"norw\",\n \"southafricanorth\": \"san\",\n \"southafricawest\": \"saw\",\n \"southcentralus\": \"scus\",\n \"southeastasia\": \"sea\",\n \"southindia\": \"sin\",\n \"swedencentral\": \"swc\",\n \"switzerlandnorth\": \"swn\",\n \"switzerlandwest\": \"sww\",\n \"uaecentral\": \"uaec\",\n \"uaenorth\": \"uaen\",\n \"uksouth\": \"uks\",\n \"ukwest\": \"ukw\",\n \"westcentralus\": \"wcus\",\n \"westeurope\": \"weu\",\n \"westindia\": \"win\",\n \"westus\": \"wus\",\n \"westus2\": \"wus2\",\n \"westus3\": \"wus3\"\n }\n}", + "naming": "[json(variables('$fxv#0'))]", + "uniqueIdShort": "[substring(parameters('uniqueId'), 0, 5)]", + "resourceTypeToken": "RES_TYPE", + "namingBase": "[format('{0}-{1}-{2}-{3}', variables('resourceTypeToken'), parameters('workloadName'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseUnique": "[format('{0}-{1}-{2}-{3}-{4}', variables('resourceTypeToken'), parameters('workloadName'), variables('uniqueIdShort'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "namingBaseNoWorkloadName": "[format('{0}-{1}-{2}', variables('resourceTypeToken'), parameters('environment'), variables('naming').regionAbbreviations[toLower(parameters('location'))])]", + "resourceTypeAbbreviations": "[variables('naming').resourceTypeAbbreviations]", + "keyVaultName": "[take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault), 24)]", + "resourceNames": { + "vnetSpoke": "[format('{0}-spoke', replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "vnetHub": "[format('{0}-hub', replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualNetwork))]", + "applicationGateway": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway)]", + "applicationGatewayPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayUserAssignedIdentity": "[format('{0}-{1}-KeyVaultSecretUser', variables('naming').resourceTypeAbbreviations.managedIdentity, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "applicationGatewayNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationGateway))]", + "pepNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.privateEndpoint))]", + "applicationInsights": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.applicationInsights)]", + "azureFirewall": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall)]", + "azureFirewallPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureFirewall))]", + "bastion": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion)]", + "bastionNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "bastionPip": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.publicIpAddress, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.bastion))]", + "containerAppsEnvironment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment)]", + "containerAppsEnvironmentNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerAppsEnvironment))]", + "containerRegistry": "[take(toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')), 50)]", + "containerRegistryPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "containerRegistryUserAssignedIdentity": "[format('{0}-{1}-AcrPull', variables('naming').resourceTypeAbbreviations.managedIdentity, toLower(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.containerRegistry), '-', '')))]", + "redisCache": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache)]", + "redisCachePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.redisCache))]", + "openAiAccount": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cognitiveAccount)]", + "openAiDeployment": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.openAiDeployment)]", + "cosmosDbNoSql": "[toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44))]", + "cosmosDbNoSqlPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(take(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.cosmosDbNoSql), 44)))]", + "frontDoorProfile": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "keyVault": "[if(endsWith(variables('keyVaultName'), '-'), take(variables('keyVaultName'), 23), variables('keyVaultName'))]", + "keyVaultPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.keyVault))]", + "logAnalyticsWorkspace": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.logAnalyticsWorkspace)]", + "routeTable": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.routeTable)]", + "serviceBus": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus)]", + "serviceBusPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.serviceBus))]", + "storageAccount": "[toLower(take(replace(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount), '-', ''), 24))]", + "storageAccountPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, toLower(replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.storageAccount)))]", + "vmJumpBox": "[replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine)]", + "vmJumpBoxNsg": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkSecurityGroup, replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "vmJumpBoxNic": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.networkInterface, replace(variables('namingBaseNoWorkloadName'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.virtualMachine))]", + "frontDoor": "[replace(variables('namingBase'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.frontDoor)]", + "azureAISearch": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch)]", + "azureAISearchPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.azureAISearch))]", + "documentIntelligence": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence)]", + "documentIntelligencePep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.documentIntelligence))]", + "eventGridSystemTopic": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic)]", + "eventGridSystemTopicPep": "[format('{0}-{1}', variables('naming').resourceTypeAbbreviations.privateEndpoint, replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSystemTopic))]", + "eventGridSubscription": "[replace(variables('namingBaseUnique'), variables('resourceTypeToken'), variables('naming').resourceTypeAbbreviations.eventGridSubscription)]" + } + }, + "resources": [], + "outputs": { + "resourcesNames": { + "type": "object", + "value": "[variables('resourceNames')]" + }, + "resourceTypeAbbreviations": { + "type": "object", + "value": "[variables('resourceTypeAbbreviations')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "containerAppsDefaultDomainName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('containerAppsEnvironmentSubscriptionId'), variables('containerAppsEnvironmentResourceGroupName')), 'Microsoft.App/managedEnvironments', variables('containerAppsEnvironmentName')), '2024-03-01').defaultDomain]" + }, + "containerAppsEnvironmentSubscriptionId": { + "value": "[variables('containerAppsEnvironmentSubscriptionId')]" + }, + "privateLinkServiceName": { + "value": "[format('{0}-{1}', reference(resourceId('Microsoft.Resources/deployments', take(format('frontDoorNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourceTypeAbbreviations.value.privateLinkService, reference(resourceId('Microsoft.Resources/deployments', take(format('frontDoorNamingDeployment-{0}', deployment().name), 64)), '2022-09-01').outputs.resourcesNames.value.frontDoor)]" + }, + "privateLinkSubnetId": { + "value": "[parameters('privateLinkSubnetId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "13579081567653218345" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The tags to be assigned to the created resources." + } + }, + "privateLinkServiceName": { + "type": "string", + "metadata": { + "description": "The name of the private link service to create." + } + }, + "privateLinkSubnetId": { + "type": "string", + "metadata": { + "description": "SubnetId used to configure private link service." + } + }, + "containerAppsDefaultDomainName": { + "type": "string", + "metadata": { + "description": "Azure Container Apps Environment Default Domain Name" + } + }, + "containerAppsEnvironmentSubscriptionId": { + "type": "string", + "metadata": { + "description": "Azure Container Apps Environment Subscription Id" + } + } + }, + "variables": { + "containerAppsDefaultDomainArray": "[split(parameters('containerAppsDefaultDomainName'), '.')]", + "containerAppsNameIdentifier": "[variables('containerAppsDefaultDomainArray')[sub(lastIndexOf(variables('containerAppsDefaultDomainArray'), parameters('location')), 1)]]", + "containerAppsManagedResourceGroup": "[format('MC_{0}-rg_{1}_{2}', variables('containerAppsNameIdentifier'), variables('containerAppsNameIdentifier'), parameters('location'))]" + }, + "resources": [ + { + "type": "Microsoft.Network/privateLinkServices", + "apiVersion": "2023-11-01", + "name": "[parameters('privateLinkServiceName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "loadBalancerFrontendIpConfigurations": [ + { + "id": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('containerAppsEnvironmentSubscriptionId'), variables('containerAppsManagedResourceGroup')), 'Microsoft.Network/loadBalancers', 'kubernetes-internal'), '2021-05-01').frontendIPConfigurations[0].id]" + } + ], + "ipConfigurations": [ + { + "name": "snet-provider-default-1", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[parameters('privateLinkSubnetId')]" + }, + "primary": true, + "privateIPAddressVersion": "IPv4" + } + } + ] + } + } + ], + "outputs": { + "privateLinkServiceId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateLinkServices', parameters('privateLinkServiceName'))]" + }, + "privateEndpointConnections": { + "type": "array", + "value": "[reference(resourceId('Microsoft.Network/privateLinkServices', parameters('privateLinkServiceName')), '2023-11-01').privateEndpointConnections]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', take(format('frontDoorNamingDeployment-{0}', deployment().name), 64))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('frontDoorDeployment-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "dep-test-cdnpmax" + }, + "location": { + "value": "Global" + }, + "sku": { + "value": "Premium_AzureFrontDoor" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "originResponseTimeoutSeconds": { + "value": 60 + }, + "endpointProperties": { + "value": { + "contentTypesToCompress": [ + "application/javascript", + "application/json", + "application/x-javascript", + "application/xml", + "text/css", + "text/html", + "text/javascript", + "text/plain" + ], + "geoFilters": [], + "isCompressionEnabled": true, + "isHttpAllowed": true, + "isHttpsAllowed": true, + "queryStringCachingBehavior": "IgnoreQueryString" + } + }, + "originGroups": "[if(not(empty(parameters('frontDoorOriginHostName'))), createObject('value', createArray(createObject('name', parameters('frontDoorOriginGroupName'), 'loadBalancingSettings', createObject('sampleSize', 4, 'successfulSamplesRequired', 3, 'additionalLatencyInMilliseconds', 50), 'healthProbeSettings', createObject('probePath', '/', 'probeRequestType', 'HEAD', 'probeProtocol', 'Https', 'probeIntervalInSeconds', 100), 'sessionAffinityState', 'Disabled', 'origins', createArray(createObject('name', parameters('frontDoorOriginName'), 'hostName', parameters('frontDoorOriginHostName'), 'httpPort', 80, 'httpsPort', 443, 'originHostHeader', parameters('frontDoorOriginHostName'), 'priority', 1, 'weight', 1000, 'enabledState', 'Enabled', 'sharedPrivateLinkResource', createObject('privateLink', createObject('id', reference(resourceId('Microsoft.Resources/deployments', format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.privateLinkServiceId.value), 'privateLinkLocation', parameters('location'), 'requestMessage', 'frontdoor'), 'enforceCertificateNameCheck', true()))))), createObject('value', createArray()))]", + "afdEndpoints": { + "value": [ + { + "name": "[parameters('frontDoorEndpointName')]", + "originGroup": "[parameters('frontDoorOriginGroupName')]", + "enabledState": "Enabled", + "routes": [ + { + "name": "[parameters('frontDoorOriginRouteName')]", + "originPath": "/", + "ruleSets": [], + "supportedProtocols": [ + "Http", + "Https" + ], + "patternsToMatch": [ + "/*" + ], + "forwardingProtocol": "HttpsOnly", + "linkToDefaultDomain": "Enabled", + "httpsRedirect": "Enabled", + "enabledState": "Enabled" + } + ] + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "10155723221995510314" + }, + "name": "CDN Profiles", + "description": "This module deploys a CDN Profile.", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the CDN profile." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Custom_Verizon", + "Premium_AzureFrontDoor", + "Premium_Verizon", + "StandardPlus_955BandWidth_ChinaCdn", + "StandardPlus_AvgBandWidth_ChinaCdn", + "StandardPlus_ChinaCdn", + "Standard_955BandWidth_ChinaCdn", + "Standard_AvgBandWidth_ChinaCdn", + "Standard_AzureFrontDoor", + "Standard_ChinaCdn", + "Standard_Microsoft", + "Standard_Verizon" + ], + "metadata": { + "description": "Required. The pricing tier (defines a CDN provider, feature list and rate) of the CDN profile." + } + }, + "originResponseTimeoutSeconds": { + "type": "int", + "defaultValue": 60, + "metadata": { + "description": "Optional. Send and receive timeout on forwarding request to the origin." + } + }, + "endpointName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the endpoint under the profile which is unique globally." + } + }, + "endpointProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Endpoint properties (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/endpoints?pivots=deployment-language-bicep#endpointproperties for details)." + } + }, + "secrets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of secret objects." + } + }, + "customDomains": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of custom domain objects." + } + }, + "originGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Conditional. Array of origin group objects. Required if the afdEndpoints is specified." + } + }, + "ruleSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of rule set objects." + } + }, + "afdEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of AFD endpoint objects." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Endpoint tags." + } + }, + "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." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.cdn-profile.{0}.{1}', replace('0.3.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" + } + } + } + } + }, + "profile": { + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "originResponseTimeoutSeconds": "[parameters('originResponseTimeoutSeconds')]" + }, + "tags": "[parameters('tags')]" + }, + "profile_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.Cdn/profiles/{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": [ + "profile" + ] + }, + "profile_roleAssignments": { + "copy": { + "name": "profile_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Cdn/profiles/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Cdn/profiles', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "profile" + ] + }, + "profile_endpoint": { + "condition": "[not(empty(parameters('endpointProperties')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-Endpoint', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('endpointName'), format('{0}-endpoint', parameters('name')))]" + }, + "properties": { + "value": "[coalesce(parameters('endpointProperties'), createObject())]" + }, + "location": { + "value": "[parameters('location')]" + }, + "profileName": { + "value": "[parameters('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.27.1.19265", + "templateHash": "6907263116726609492" + }, + "name": "CDN Profiles Endpoints", + "description": "This module deploys a CDN Profile Endpoint.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the endpoint under the profile which is unique globally." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Resource location." + } + }, + "properties": { + "type": "object", + "metadata": { + "description": "Required. Endpoint properties (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/endpoints?pivots=deployment-language-bicep#endpointproperties for details)." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Endpoint tags." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2021-06-01", + "name": "[parameters('profileName')]" + }, + "endpoint": { + "type": "Microsoft.Cdn/profiles/endpoints", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": "[parameters('properties')]", + "tags": "[parameters('tags')]", + "dependsOn": [ + "profile" + ] + }, + "endpoint_origins": { + "copy": { + "name": "endpoint_origins", + "count": "[length(parameters('properties').origins)]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-origins-{1}', parameters('name'), parameters('properties').origins[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "profileName": { + "value": "[parameters('profileName')]" + }, + "endpointName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('properties').origins[copyIndex()].name]" + }, + "hostName": { + "value": "[parameters('properties').origins[copyIndex()].properties.hostName]" + }, + "httpPort": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'httpPort')]" + }, + "httpsPort": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'httpsPort')]" + }, + "enabled": { + "value": "[parameters('properties').origins[copyIndex()].properties.enabled]" + }, + "priority": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'priority')]" + }, + "weight": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'weight')]" + }, + "originHostHeader": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'originHostHeader')]" + }, + "privateLinkAlias": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'privateLinkAlias')]" + }, + "privateLinkLocation": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'privateLinkLocation')]" + }, + "privateLinkResourceId": { + "value": "[tryGet(parameters('properties').origins[copyIndex()].properties, 'privateLinkResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "2422401530872933264" + }, + "name": "CDN Profiles Endpoints Origins", + "description": "This module deploys a CDN Profile Endpoint Origin.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "endpointName": { + "type": "string", + "metadata": { + "description": "Required. The name of the CDN Endpoint." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the origin." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether the origin is enabled for load balancing." + } + }, + "hostName": { + "type": "string", + "metadata": { + "description": "Required. The hostname of the origin." + } + }, + "httpPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "Optional. The HTTP port of the origin." + } + }, + "httpsPort": { + "type": "int", + "defaultValue": 443, + "metadata": { + "description": "Optional. The HTTPS port of the origin." + } + }, + "priority": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Conditional. The priority of origin in given origin group for load balancing. Required if `weight` is provided." + } + }, + "weight": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Conditional. The weight of the origin used for load balancing. Required if `priority` is provided." + } + }, + "privateLinkAlias": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The private link alias of the origin. Required if privateLinkLocation is provided." + } + }, + "privateLinkLocation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The private link location of the origin. Required if privateLinkAlias is provided." + } + }, + "privateLinkResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The private link resource ID of the origin." + } + }, + "originHostHeader": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The host header value sent to the origin." + } + }, + "profileName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the CDN profile. Default to \"default\"." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2021-06-01", + "name": "[parameters('profileName')]" + }, + "endpoint": { + "existing": true, + "type": "Microsoft.Cdn/profiles/endpoints", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('endpointName'))]", + "dependsOn": [ + "profile" + ] + }, + "origin": { + "type": "Microsoft.Cdn/profiles/endpoints/origins", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}/{2}', parameters('profileName'), parameters('endpointName'), parameters('name'))]", + "properties": "[union(createObject('hostName', parameters('hostName'), 'httpPort', parameters('httpPort'), 'enabled', parameters('enabled'), 'httpsPort', parameters('httpsPort')), if(or(greater(parameters('priority'), 0), greater(parameters('weight'), 0)), createObject('priority', parameters('priority'), 'weight', parameters('weight')), createObject()), if(and(not(empty(parameters('privateLinkAlias'))), not(empty(parameters('privateLinkLocation')))), createObject('privateLinkAlias', parameters('privateLinkAlias'), 'privateLinkLocation', parameters('privateLinkLocation')), createObject()), if(not(empty(parameters('privateLinkResourceId'))), createObject('privateLinkResourceId', parameters('privateLinkResourceId')), createObject()), if(not(empty(parameters('originHostHeader'))), createObject('originHostHeader', parameters('originHostHeader')), createObject()))]", + "dependsOn": [ + "endpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the endpoint." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the endpoint." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/endpoints/origins', parameters('profileName'), parameters('endpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the endpoint was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('endpoint', '2021-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "endpoint", + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the endpoint." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the endpoint." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/endpoints', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the endpoint was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('endpoint', '2021-06-01', 'full').location]" + }, + "endpointProperties": { + "type": "object", + "metadata": { + "description": "The properties of the endpoint." + }, + "value": "[reference('endpoint')]" + } + } + } + }, + "dependsOn": [ + "profile" + ] + }, + "profile_secrets": { + "copy": { + "name": "profile_secrets", + "count": "[length(parameters('secrets'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-Secret-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('secrets')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "type": { + "value": "[parameters('secrets')[copyIndex()].type]" + }, + "secretSourceResourceId": { + "value": "[parameters('secrets')[copyIndex()].secretSourceResourceId]" + }, + "subjectAlternativeNames": { + "value": "[tryGet(parameters('secrets')[copyIndex()], 'subjectAlternativeNames')]" + }, + "useLatestVersion": { + "value": "[tryGet(parameters('secrets')[copyIndex()], 'useLatestVersion')]" + }, + "secretVersion": { + "value": "[parameters('secrets')[copyIndex()].secretVersion]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "8969856235939919174" + }, + "name": "CDN Profiles Secret", + "description": "This module deploys a CDN Profile Secret.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secrect." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "type": { + "type": "string", + "defaultValue": "AzureFirstPartyManagedCertificate", + "allowedValues": [ + "AzureFirstPartyManagedCertificate", + "CustomerCertificate", + "ManagedCertificate", + "UrlSigningKey" + ], + "metadata": { + "description": "Required. The type of the secrect." + } + }, + "secretSourceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the secret source. Required if the `type` is \"CustomerCertificate\"." + } + }, + "secretVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The version of the secret." + } + }, + "subjectAlternativeNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The subject alternative names of the secrect." + } + }, + "useLatestVersion": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether to use the latest version of the secrect." + } + } + }, + "resources": [ + { + "type": "Microsoft.Cdn/profiles/secrets", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "parameters": "[if(equals(parameters('type'), 'CustomerCertificate'), createObject('type', parameters('type'), 'secretSource', createObject('id', parameters('secretSourceResourceId')), 'secretVersion', parameters('secretVersion'), 'subjectAlternativeNames', parameters('subjectAlternativeNames'), 'useLatestVersion', parameters('useLatestVersion')), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secrect." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secrect." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/secrets', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "profile" + ] + }, + "profile_customDomains": { + "copy": { + "name": "profile_customDomains", + "count": "[length(parameters('customDomains'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-CustomDomain-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('customDomains')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "hostName": { + "value": "[parameters('customDomains')[copyIndex()].hostName]" + }, + "azureDnsZoneResourceId": { + "value": "[tryGet(parameters('customDomains')[copyIndex()], 'azureDnsZoneResourceId')]" + }, + "extendedProperties": { + "value": "[tryGet(parameters('customDomains')[copyIndex()], 'extendedProperties')]" + }, + "certificateType": { + "value": "[parameters('customDomains')[copyIndex()].certificateType]" + }, + "minimumTlsVersion": { + "value": "[tryGet(parameters('customDomains')[copyIndex()], 'minimumTlsVersion')]" + }, + "preValidatedCustomDomainResourceId": { + "value": "[tryGet(parameters('customDomains')[copyIndex()], 'preValidatedCustomDomainResourceId')]" + }, + "secretName": { + "value": "[tryGet(parameters('customDomains')[copyIndex()], 'secretName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "4987993668641975476" + }, + "name": "CDN Profiles Custom Domains", + "description": "This module deploys a CDN Profile Custom Domains.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the custom domain." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the CDN profile." + } + }, + "hostName": { + "type": "string", + "metadata": { + "description": "Required. The host name of the domain. Must be a domain name." + } + }, + "azureDnsZoneResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optonal. Resource reference to the Azure DNS zone." + } + }, + "extendedProperties": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Key-Value pair representing migration properties for domains." + } + }, + "preValidatedCustomDomainResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource reference to the Azure resource where custom domain ownership was prevalidated." + } + }, + "certificateType": { + "type": "string", + "allowedValues": [ + "CustomerCertificate", + "ManagedCertificate" + ], + "metadata": { + "description": "Required. The type of the certificate used for secure delivery." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS12", + "allowedValues": [ + "TLS10", + "TLS12" + ], + "metadata": { + "description": "Optional. The minimum TLS version required for the custom domain. Default value: TLS12." + } + }, + "secretName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the secret. ie. subs/rg/profile/secret." + } + } + }, + "resources": [ + { + "type": "Microsoft.Cdn/profiles/customDomains", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "azureDnsZone": "[if(not(empty(parameters('azureDnsZoneResourceId'))), createObject('id', parameters('azureDnsZoneResourceId')), null())]", + "extendedProperties": "[if(not(empty(parameters('extendedProperties'))), parameters('extendedProperties'), null())]", + "hostName": "[parameters('hostName')]", + "preValidatedCustomDomainResourceId": "[if(not(empty(parameters('preValidatedCustomDomainResourceId'))), createObject('id', parameters('preValidatedCustomDomainResourceId')), null())]", + "tlsSettings": { + "certificateType": "[parameters('certificateType')]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "secret": "[if(not(empty(parameters('secretName'))), createObject('id', resourceId('Microsoft.Cdn/profiles/secrets', parameters('profileName'), parameters('secretName'))), null())]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the custom domain." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the custom domain." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/customDomains', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the custom domain was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "profile", + "profile_secrets" + ] + }, + "profile_originGroups": { + "copy": { + "name": "profile_originGroups", + "count": "[length(parameters('originGroups'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-OriginGroup-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('originGroups')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "loadBalancingSettings": { + "value": "[parameters('originGroups')[copyIndex()].loadBalancingSettings]" + }, + "healthProbeSettings": { + "value": "[tryGet(parameters('originGroups')[copyIndex()], 'healthProbeSettings')]" + }, + "sessionAffinityState": { + "value": "[tryGet(parameters('originGroups')[copyIndex()], 'sessionAffinityState')]" + }, + "trafficRestorationTimeToHealedOrNewEndpointsInMinutes": { + "value": "[tryGet(parameters('originGroups')[copyIndex()], 'trafficRestorationTimeToHealedOrNewEndpointsInMinutes')]" + }, + "origins": { + "value": "[parameters('originGroups')[copyIndex()].origins]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "15134351122358254660" + }, + "name": "CDN Profiles Origin Group", + "description": "This module deploys a CDN Profile Origin Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the origin group." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the CDN profile." + } + }, + "healthProbeSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Health probe settings to the origin that is used to determine the health of the origin." + } + }, + "loadBalancingSettings": { + "type": "object", + "metadata": { + "description": "Required. Load balancing settings for a backend pool." + } + }, + "sessionAffinityState": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether to allow session affinity on this host." + } + }, + "trafficRestorationTimeToHealedOrNewEndpointsInMinutes": { + "type": "int", + "defaultValue": 10, + "metadata": { + "description": "Optional. Time in minutes to shift the traffic to the endpoint gradually when an unhealthy endpoint comes healthy or a new endpoint is added. Default is 10 mins." + } + }, + "origins": { + "type": "array", + "metadata": { + "description": "Required. The list of origins within the origin group." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "originGroup": { + "type": "Microsoft.Cdn/profiles/originGroups", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "healthProbeSettings": "[parameters('healthProbeSettings')]", + "loadBalancingSettings": "[parameters('loadBalancingSettings')]", + "sessionAffinityState": "[parameters('sessionAffinityState')]", + "trafficRestorationTimeToHealedOrNewEndpointsInMinutes": "[parameters('trafficRestorationTimeToHealedOrNewEndpointsInMinutes')]" + }, + "dependsOn": [ + "profile" + ] + }, + "originGroup_origins": { + "copy": { + "name": "originGroup_origins", + "count": "[length(parameters('origins'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-OriginGroup-Origin-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('origins')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('profileName')]" + }, + "hostName": { + "value": "[parameters('origins')[copyIndex()].hostName]" + }, + "originGroupName": { + "value": "[parameters('name')]" + }, + "enabledState": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'enabledState')]" + }, + "enforceCertificateNameCheck": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'enforceCertificateNameCheck')]" + }, + "httpPort": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'httpPort')]" + }, + "httpsPort": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'httpsPort')]" + }, + "originHostHeader": { + "value": "[coalesce(tryGet(parameters('origins')[copyIndex()], 'originHostHeader'), parameters('origins')[copyIndex()].hostName)]" + }, + "priority": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'priority')]" + }, + "weight": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'weight')]" + }, + "sharedPrivateLinkResource": { + "value": "[tryGet(parameters('origins')[copyIndex()], 'sharedPrivateLinkResource')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "13403133934817379620" + }, + "name": "CDN Profiles Origin", + "description": "This module deploys a CDN Profile Origin.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the origion." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the CDN profile." + } + }, + "originGroupName": { + "type": "string", + "metadata": { + "description": "Required. The name of the group." + } + }, + "enabledState": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether to enable health probes to be made against backends defined under backendPools. Health probes can only be disabled if there is a single enabled backend in single enabled backend pool." + } + }, + "enforceCertificateNameCheck": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether to enable certificate name check at origin level." + } + }, + "hostName": { + "type": "string", + "metadata": { + "description": "Required. The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported.This should be unique across all origins in an endpoint." + } + }, + "httpPort": { + "type": "int", + "defaultValue": 80, + "metadata": { + "description": "Optional. The value of the HTTP port. Must be between 1 and 65535." + } + }, + "httpsPort": { + "type": "int", + "defaultValue": 443, + "metadata": { + "description": "Optional. The value of the HTTPS port. Must be between 1 and 65535." + } + }, + "originHostHeader": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin hostname by default. This overrides the host header defined at Endpoint." + } + }, + "priority": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "Optional. Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any lower priority origin is healthy.Must be between 1 and 5." + } + }, + "sharedPrivateLinkResource": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the private link resource for private origin." + } + }, + "weight": { + "type": "int", + "defaultValue": 1000, + "metadata": { + "description": "Optional. Weight of the origin in given origin group for load balancing. Must be between 1 and 1000." + } + } + }, + "resources": { + "profile::originGroup": { + "existing": true, + "type": "Microsoft.Cdn/profiles/originGroups", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('originGroupName'))]", + "dependsOn": [ + "profile" + ] + }, + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "origin": { + "type": "Microsoft.Cdn/profiles/originGroups/origins", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}/{2}', parameters('profileName'), parameters('originGroupName'), parameters('name'))]", + "properties": { + "enabledState": "[parameters('enabledState')]", + "enforceCertificateNameCheck": "[parameters('enforceCertificateNameCheck')]", + "hostName": "[parameters('hostName')]", + "httpPort": "[parameters('httpPort')]", + "httpsPort": "[parameters('httpsPort')]", + "originHostHeader": "[parameters('originHostHeader')]", + "priority": "[parameters('priority')]", + "sharedPrivateLinkResource": "[parameters('sharedPrivateLinkResource')]", + "weight": "[parameters('weight')]" + }, + "dependsOn": [ + "profile::originGroup" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the origin." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the origin." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/originGroups/origins', parameters('profileName'), parameters('originGroupName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the origin was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "originGroup" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the origin group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the origin group." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/originGroups', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the origin group was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('profile', '2023-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "profile" + ] + }, + "profile_ruleSets": { + "copy": { + "name": "profile_ruleSets", + "count": "[length(parameters('ruleSets'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-RuleSet-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('ruleSets')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[parameters('ruleSets')[copyIndex()].rules]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "15489136897524188469" + }, + "name": "CDN Profiles Rule Sets", + "description": "This module deploys a CDN Profile rule set.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the rule set." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the CDN profile." + } + }, + "rules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optinal. The rules to apply to the rule set." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "ruleSet": { + "type": "Microsoft.Cdn/profiles/ruleSets", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "dependsOn": [ + "profile" + ] + }, + "ruleSet_rules": { + "copy": { + "name": "ruleSet_rules", + "count": "[length(coalesce(parameters('rules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RuleSet-Rule-{1}-{2}', uniqueString(deployment().name), coalesce(parameters('rules'), createArray())[copyIndex()].name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "profileName": { + "value": "[parameters('profileName')]" + }, + "ruleSetName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('rules'), createArray())[copyIndex()].name]" + }, + "order": { + "value": "[coalesce(parameters('rules'), createArray())[copyIndex()].order]" + }, + "actions": { + "value": "[tryGet(coalesce(parameters('rules'), createArray())[copyIndex()], 'actions')]" + }, + "conditions": { + "value": "[tryGet(coalesce(parameters('rules'), createArray())[copyIndex()], 'conditions')]" + }, + "matchProcessingBehavior": { + "value": "[tryGet(coalesce(parameters('rules'), createArray())[copyIndex()], 'matchProcessingBehavior')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "16996204249819398780" + }, + "name": "CDN Profiles Rules", + "description": "This module deploys a CDN Profile rule.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the rule." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the profile." + } + }, + "ruleSetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the rule set." + } + }, + "order": { + "type": "int", + "metadata": { + "description": "Required. The order in which this rule will be applied. Rules with a lower order are applied before rules with a higher order." + } + }, + "actions": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of actions that are executed when all the conditions of a rule are satisfied." + } + }, + "conditions": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of conditions that must be matched for the actions to be executed." + } + }, + "matchProcessingBehavior": { + "type": "string", + "defaultValue": "Continue", + "allowedValues": [ + "Continue", + "Stop" + ], + "metadata": { + "description": "Optional. If this rule is a match should the rules engine continue running the remaining rules or stop. If not present, defaults to Continue." + } + } + }, + "resources": { + "profile::ruleSet": { + "existing": true, + "type": "Microsoft.Cdn/profiles/ruleSets", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('ruleSetName'))]", + "dependsOn": [ + "profile" + ] + }, + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "rule": { + "type": "Microsoft.Cdn/profiles/ruleSets/rules", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}/{2}', parameters('profileName'), parameters('ruleSetName'), parameters('name'))]", + "properties": { + "order": "[parameters('order')]", + "actions": "[parameters('actions')]", + "conditions": "[parameters('conditions')]", + "matchProcessingBehavior": "[parameters('matchProcessingBehavior')]" + }, + "dependsOn": [ + "profile::ruleSet" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the rule." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/ruleSets/rules', parameters('profileName'), parameters('ruleSetName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the custom domain was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the rule set." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the rule set." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/ruleSets', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the custom domain was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "profile" + ] + }, + "profile_afdEndpoints": { + "copy": { + "name": "profile_afdEndpoints", + "count": "[length(parameters('afdEndpoints'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-AfdEndpoint-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('afdEndpoints')[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "autoGeneratedDomainNameLabelScope": { + "value": "[tryGet(parameters('afdEndpoints')[copyIndex()], 'autoGeneratedDomainNameLabelScope')]" + }, + "enabledState": { + "value": "[tryGet(parameters('afdEndpoints')[copyIndex()], 'enabledState')]" + }, + "routes": { + "value": "[tryGet(parameters('afdEndpoints')[copyIndex()], 'routes')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('afdEndpoints')[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "12280139305247236649" + }, + "name": "CDN Profiles AFD Endpoints", + "description": "This module deploys a CDN Profile AFD Endpoint.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AFD Endpoint." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the AFD Endpoint." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the AFD Endpoint." + } + }, + "autoGeneratedDomainNameLabelScope": { + "type": "string", + "defaultValue": "TenantReuse", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Optional. Indicates the endpoint name reuse scope. The default value is TenantReuse." + } + }, + "enabledState": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Indicates whether the AFD Endpoint is enabled. The default value is Enabled." + } + }, + "routes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of routes for this AFD Endpoint." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "afdEndpoint": { + "type": "Microsoft.Cdn/profiles/afdEndpoints", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "autoGeneratedDomainNameLabelScope": "[parameters('autoGeneratedDomainNameLabelScope')]", + "enabledState": "[parameters('enabledState')]" + }, + "dependsOn": [ + "profile" + ] + }, + "afdEndpoint_routes": { + "copy": { + "name": "afdEndpoint_routes", + "count": "[length(coalesce(parameters('routes'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-AfdEndpoint-Route', uniqueString(deployment().name, coalesce(parameters('routes'), createArray())[copyIndex()].name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('routes'), createArray())[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('profileName')]" + }, + "afdEndpointName": { + "value": "[parameters('name')]" + }, + "cacheConfiguration": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'cacheConfiguration')]" + }, + "customDomainNames": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'customDomainNames')]" + }, + "enabledState": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'enabledState')]" + }, + "forwardingProtocol": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'forwardingProtocol')]" + }, + "httpsRedirect": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'httpsRedirect')]" + }, + "linkToDefaultDomain": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'linkToDefaultDomain')]" + }, + "originGroupName": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'originGroupName')]" + }, + "originPath": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'originPath')]" + }, + "patternsToMatch": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'patternsToMatch')]" + }, + "ruleSets": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'ruleSets')]" + }, + "supportedProtocols": { + "value": "[tryGet(coalesce(parameters('routes'), createArray())[copyIndex()], 'supportedProtocols')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "10229922284695085147" + }, + "name": "CDN Profiles AFD Endpoint Route", + "description": "This module deploys a CDN Profile AFD Endpoint route.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the route." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent CDN profile." + } + }, + "afdEndpointName": { + "type": "string", + "metadata": { + "description": "Required. The name of the AFD endpoint." + } + }, + "cacheConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The caching configuration for this route. To disable caching, do not provide a cacheConfiguration object." + } + }, + "customDomainNames": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The names of the custom domains. The custom domains must be defined in the profile customDomains array." + } + }, + "forwardingProtocol": { + "type": "string", + "defaultValue": "MatchRequest", + "allowedValues": [ + "HttpOnly", + "HttpsOnly", + "MatchRequest" + ], + "metadata": { + "description": "Optional. The protocol this rule will use when forwarding traffic to backends." + } + }, + "enabledState": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether this route is enabled." + } + }, + "httpsRedirect": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether to automatically redirect HTTP traffic to HTTPS traffic." + } + }, + "linkToDefaultDomain": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether this route will be linked to the default endpoint domain." + } + }, + "originGroupName": { + "type": "string", + "metadata": { + "description": "Required. The name of the origin group. The origin group must be defined in the profile originGroups." + } + }, + "originPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A directory path on the origin that AzureFrontDoor can use to retrieve content from, e.g. contoso.cloudapp.net/originpath." + } + }, + "patternsToMatch": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The route patterns of the rule." + } + }, + "ruleSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The rule sets of the rule. The rule sets must be defined in the profile ruleSets." + } + }, + "supportedProtocols": { + "type": "array", + "nullable": true, + "allowedValues": [ + "Http", + "Https" + ], + "metadata": { + "description": "Optional. The supported protocols of the rule." + } + } + }, + "resources": { + "profile::afdEndpoint": { + "existing": true, + "type": "Microsoft.Cdn/profiles/afdEndpoints", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('afdEndpointName'))]", + "dependsOn": [ + "profile" + ] + }, + "profile::customDomains": { + "copy": { + "name": "customDomains", + "count": "[length(coalesce(parameters('customDomainNames'), createArray()))]" + }, + "existing": true, + "type": "Microsoft.Cdn/profiles/customDomains", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), coalesce(parameters('customDomainNames'), createArray())[copyIndex()])]", + "dependsOn": [ + "profile" + ] + }, + "profile::originGroup": { + "existing": true, + "type": "Microsoft.Cdn/profiles/originGroups", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('originGroupName'))]", + "dependsOn": [ + "profile" + ] + }, + "profile::ruleSet": { + "copy": { + "name": "ruleSet", + "count": "[length(parameters('ruleSets'))]" + }, + "existing": true, + "type": "Microsoft.Cdn/profiles/ruleSets", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('ruleSets')[copyIndex()].name)]", + "dependsOn": [ + "profile" + ] + }, + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "route": { + "type": "Microsoft.Cdn/profiles/afdEndpoints/routes", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}/{2}', parameters('profileName'), parameters('afdEndpointName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "customDomains", + "count": "[length(range(0, length(coalesce(parameters('customDomainNames'), createArray()))))]", + "input": { + "id": "[resourceId('Microsoft.Cdn/profiles/customDomains', parameters('profileName'), coalesce(parameters('customDomainNames'), createArray())[range(0, length(coalesce(parameters('customDomainNames'), createArray())))[copyIndex('customDomains')]])]" + } + }, + { + "name": "ruleSets", + "count": "[length(parameters('ruleSets'))]", + "input": { + "id": "[resourceId('Microsoft.Cdn/profiles/ruleSets', parameters('profileName'), parameters('ruleSets')[copyIndex('ruleSets')].name)]" + } + } + ], + "cacheConfiguration": "[parameters('cacheConfiguration')]", + "enabledState": "[parameters('enabledState')]", + "forwardingProtocol": "[parameters('forwardingProtocol')]", + "httpsRedirect": "[parameters('httpsRedirect')]", + "linkToDefaultDomain": "[parameters('linkToDefaultDomain')]", + "originGroup": { + "id": "[resourceId('Microsoft.Cdn/profiles/originGroups', parameters('profileName'), parameters('originGroupName'))]" + }, + "originPath": "[parameters('originPath')]", + "patternsToMatch": "[parameters('patternsToMatch')]", + "supportedProtocols": "[parameters('supportedProtocols')]" + }, + "dependsOn": [ + "profile::afdEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the route." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the route." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/afdEndpoints/routes', parameters('profileName'), parameters('afdEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the route was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "afdEndpoint", + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the AFD Endpoint." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource id of the AFD Endpoint." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/afdEndpoints', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the endpoint was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('afdEndpoint', '2023-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "profile", + "profile_customDomains", + "profile_originGroups", + "profile_ruleSets" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the CDN profile." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the CDN profile." + }, + "value": "[resourceId('Microsoft.Cdn/profiles', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the CDN profile is deployed." + }, + "value": "[resourceGroup().name]" + }, + "profileType": { + "type": "string", + "metadata": { + "description": "The type of the CDN profile." + }, + "value": "Microsoft.Cdn/profiles" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('profile', '2023-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id)))]" + ] + } + ], + "outputs": { + "frontDoorResourceId": { + "type": "string", + "metadata": { + "description": "The ID of the front door resource." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('frontDoorDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.resourceId.value]" + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "The ID of the private link service." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.privateLinkServiceId.value]" + }, + "privateLinkEndpointConnectionId": { + "type": "string", + "metadata": { + "description": "The ID of the private link endpoint connection to approve." + }, + "value": "[if(greater(length(reference(resourceId('Microsoft.Resources/deployments', format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.privateEndpointConnections.value), 0), filter(reference(resourceId('Microsoft.Resources/deployments', format('privateLinkServiceFrontDoorDeployment-{0}', uniqueString(resourceGroup().id))), '2022-09-01').outputs.privateEndpointConnections.value, lambda('connection', equals(lambdaVariables('connection').properties.privateLinkServiceConnectionState.description, 'frontdoor')))[0].id, '')]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('sampleApplication-{0}-deployment', deployment().name), 64))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64))]" + ] + } + ], + "outputs": { + "spokeResourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Spoke resource group." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeResourceGroupName.value]" + }, + "spokeVNetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Spoke Virtual Network." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeVNetId.value]" + }, + "spokeVnetName": { + "type": "string", + "metadata": { + "description": "The name of the Spoke Virtual Network." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeVNetName.value]" + }, + "spokeInfraSubnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Spoke Infrastructure Subnet." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeInfraSubnetId.value]" + }, + "spokeInfraSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the Spoke Infrastructure Subnet." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeInfraSubnetName.value]" + }, + "spokePrivateEndpointsSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the Spoke Private Endpoints Subnet." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokePrivateEndpointsSubnetName.value]" + }, + "spokeApplicationGatewaySubnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Spoke Application Gateway Subnet. If \"spokeApplicationGatewaySubnetAddressPrefix\" is empty, the subnet will not be created and the value returned is empty." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeApplicationGatewaySubnetId.value]" + }, + "spokeApplicationGatewaySubnetName": { + "type": "string", + "metadata": { + "description": "The name of the Spoke Application Gateway Subnet. If \"spokeApplicationGatewaySubnetAddressPrefix\" is empty, the subnet will not be created and the value returned is empty." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.spokeApplicationGatewaySubnetName.value]" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics workspace created in the spoke vnet." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.logAnalyticsWorkspaceId.value]" + }, + "vmJumpBoxName": { + "type": "string", + "metadata": { + "description": "The name of the jump box virtual machine." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', take(format('spoke-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.vmJumpBoxName.value]" + }, + "containerRegistryResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container registry." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryId.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name of the container registry." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryName.value]" + }, + "containerRegistryLoginServer": { + "type": "string", + "metadata": { + "description": "The name of the container registry login server." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryLoginServer.value]" + }, + "containerRegistryUserAssignedIdentityResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned managed identity for the container registry to be able to pull images from it." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerRegistryUserAssignedIdentityId.value]" + }, + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.keyVaultResourceId.value]" + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the Azure key vault." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('supportingServices-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.keyVaultName.value]" + }, + "applicationGatewayResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Application Gateway." + }, + "value": "[if(equals(parameters('exposeContainerAppsWith'), 'applicationGateway'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('applicationGateway-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.applicationGatewayResourceId.value, '')]" + }, + "applicationGatewayFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the Azure Application Gateway." + }, + "value": "[if(equals(parameters('exposeContainerAppsWith'), 'applicationGateway'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('applicationGateway-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.applicationGatewayFqdn.value, '')]" + }, + "applicationGatewayPublicIp": { + "type": "string", + "metadata": { + "description": "The public IP address of the Azure Application Gateway." + }, + "value": "[if(equals(parameters('exposeContainerAppsWith'), 'applicationGateway'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('applicationGateway-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.applicationGatewayPublicIp.value, '')]" + }, + "containerAppsEnvironmentResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container apps environment." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerAppsEnvironmentId.value]" + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "The name of the container apps environment." + }, + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.containerAppsEnvironmentName.value]" + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of application Insights instance." + }, + "value": "[if(parameters('enableApplicationInsights'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('rgSpokeName')), 'Microsoft.Resources/deployments', take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)), '2022-09-01').outputs.applicationInsightsName.value, '')]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/app-gateway-cert.bicep b/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/app-gateway-cert.bicep new file mode 100644 index 0000000000..b411ca2bbd --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/app-gateway-cert.bicep @@ -0,0 +1,53 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +param keyVaultName string + +param appGatewayUserAssignedIdentityPrincipalId string + +param appGatewayCertificateKeyName string + +param appGatewayCertificateData string + +// ------------------ +// VARIABLES +// ------------------ + +var keyVaultSecretUserRoleGuid = '4633458b-17de-408a-b874-0445c86b69e6' + +// ------------------ +// RESOURCES +// ------------------ + +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: keyVaultName +} + +//TODO: this needs to be updated to use the AVM module when it is available +resource sslCertSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + parent: keyVault + name: appGatewayCertificateKeyName + properties: { + value: appGatewayCertificateData + contentType: 'application/x-pkcs12' + attributes: { + enabled: true + } + } +} + +resource keyvaultSecretUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, keyVault.id, appGatewayUserAssignedIdentityPrincipalId, 'KeyVaultSecretUser') + scope: sslCertSecret + properties: { + principalId: appGatewayUserAssignedIdentityPrincipalId + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretUserRoleGuid) + principalType: 'ServicePrincipal' + } +} + +// Using SecretUri instead of SecretUriWithVersion to avoid having to update the App Gateway configuration when the secret version changes +output SecretUri string = sslCertSecret.properties.secretUri diff --git a/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/deploy.app-gateway.bicep b/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/deploy.app-gateway.bicep new file mode 100644 index 0000000000..54726eed7f --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/application-gateway/deploy.app-gateway.bicep @@ -0,0 +1,402 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('The location where the resources will be created. This needs to be the same region as the spoke.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Defines whether to enable telemetry for the modules deployment.') +param enableTelemetry bool + +@description('The FQDN of the Application Gateawy. Must match the TLS certificate.') +param applicationGatewayFqdn string + +@description('The existing subnet resource ID to use for Application Gateway.') +param applicationGatewaySubnetId string + +@description('The FQDN of the primary backend endpoint.') +param applicationGatewayPrimaryBackendEndFqdn string + +@description('The path to use for Application Gateway\'s backend health probe.') +param appGatewayBackendHealthProbePath string = '/' + +@description('The base64 encoded certificate to use for Application Gateway certificate. If this is provided, the certificate will be added to the Key Vault.') +@secure() +param base64Certificate string = '' + +@description('The name of the certificate key to use for Application Gateway certificate.') +param applicationGatewayCertificateKeyName string + +@description('The resource ID of the exsiting Log Analytics workload for diagnostic settngs, or nothing if you don\'t need any.') +param applicationGatewayLogAnalyticsId string = '' + +@description('The resource ID of the existing Key Vault which contains Application Gateway\'s cert.') +param keyVaultId string + +@description('Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false.') +param deployZoneRedundantResources bool = true + +@description('Optional. DDoS protection mode for the Public IP of the Application Gateway. See https://learn.microsoft.com/azure/ddos-protection/ddos-protection-sku-comparison#skus') +param enableDdosProtection bool = false + +// ------------------ +// VARIABLES +// ------------------ + +var keyVaultIdTokens = split(keyVaultId, '/') + +@description('The subscription ID of the existing Key Vault.') +var keyVaultSubscriptionId = keyVaultIdTokens[2] + +@description('The name of the resource group containing the existing Key Vault.') +var keyVaultResourceGroupName = keyVaultIdTokens[4] + +@description('The name of the existing Key Vault.') +var keyVaultName = keyVaultIdTokens[8] + +// ------------------ +// RESOURCES +// ------------------ + +@description('User-configured naming rules') +module naming '../naming/naming.module.bicep' = { + name: take('agwNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(resourceGroup().id) + environment: environment + workloadName: workloadName + location: location + } +} + +// TODO: Check if this is required if enableApplicationCertificate is false +@description('A user-assigned managed identity that enables Application Gateway to access Key Vault for its TLS certs.') +module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = { + name: take('appGwUserAssignedIdentity-Deployment-${uniqueString(resourceGroup().id)}', 64) + + params: { + name: naming.outputs.resourcesNames.applicationGatewayUserAssignedIdentity + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + +// => Key Vault User Assigned Identity, Secret & Role Assignement for certificate +// As of today, App Gateway does not supports "System Managed Identity" for Key Vault +// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#key-vault-secrets-user + +// => Certificates (supports only 1 for now) + +// @description('Adds the PFX file into Azure Key Vault for consumption by Application Gateway.') +module appGatewayAddCertificates 'app-gateway-cert.bicep' = if (!empty(base64Certificate)) { + name: take('appGatewayAddCertificates-Deployment-${uniqueString(resourceGroup().id)}', 64) + scope: resourceGroup(keyVaultSubscriptionId, keyVaultResourceGroupName) + params: { + keyVaultName: keyVaultName + appGatewayCertificateData: base64Certificate + appGatewayCertificateKeyName: applicationGatewayCertificateKeyName + appGatewayUserAssignedIdentityPrincipalId: userAssignedIdentity.outputs.principalId + } +} + +module applicationGatewayPublicIp 'br/public:avm/res/network/public-ip-address:0.4.1' = { + name: take('applicationGatewayPublicIp-Deployment-${uniqueString(resourceGroup().id)}', 64) + params: { + location: location + name: naming.outputs.resourcesNames.applicationGatewayPip + tags: tags + enableTelemetry: enableTelemetry + skuName: 'Standard' + publicIPAllocationMethod: 'Static' + ddosSettings: enableDdosProtection + ? { + protectionMode: 'Enabled' + } + : null + zones: (deployZoneRedundantResources) + ? [ + 1 + 2 + 3 + ] + : [] + diagnosticSettings: [ + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'publicIpDiagnosticSettings' + workspaceResourceId: applicationGatewayLogAnalyticsId + } + ] + } +} + +@description('The Application Gateway.') +module applicationGateway 'br/public:avm/res/network/application-gateway:0.1.0' = { + name: take('applicationGateway-Deployment-${uniqueString(resourceGroup().id)}', 64) + params: { + // Required parameters + name: naming.outputs.resourcesNames.applicationGateway + enableTelemetry: enableTelemetry + // Non-required parameters + backendAddressPools: [ + { + name: 'acaServiceBackend' + properties: { + backendAddresses: (!empty(applicationGatewayPrimaryBackendEndFqdn)) + ? [ + { + fqdn: applicationGatewayPrimaryBackendEndFqdn + } + ] + : null + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'https' + properties: { + port: 443 + protocol: 'Https' + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + requestTimeout: 20 + probe: (!empty(applicationGatewayPrimaryBackendEndFqdn)) + ? { + id: resourceId( + 'Microsoft.Network/applicationGateways/probes', + naming.outputs.resourcesNames.applicationGateway, + 'webProbe' + ) + } + : null + } + } + ] + diagnosticSettings: [ + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + workspaceResourceId: applicationGatewayLogAnalyticsId + } + ] + enableHttp2: true + frontendIPConfigurations: [ + { + name: 'appGwPublicFrontendIp' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: applicationGatewayPublicIp.outputs.resourceId + } + } + } + ] + frontendPorts: (!empty(base64Certificate)) + ? [ + { + name: 'port_443' + properties: { + port: 443 + } + } + { + name: 'port_80' + properties: { + port: 80 + } + } + ] + : [ + { + name: 'port_80' + properties: { + port: 80 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'appGatewayIpConfig' + properties: { + subnet: { + id: applicationGatewaySubnetId + } + } + } + ] + httpListeners: (empty(base64Certificate)) + ? [ + { + name: 'httpListener' + properties: { + frontendIPConfiguration: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/frontendIPConfigurations/appGwPublicFrontendIp' + } + frontendPort: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/frontendPorts/port_80' + } + protocol: 'Http' + hostnames: [] + requireServerNameIndication: false + } + } + ] + : [ + { + name: 'httpsListener' + properties: { + frontendIPConfiguration: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/frontendIPConfigurations/appGwPublicFrontendIp' + } + frontendPort: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/frontendPorts/port_443' + } + protocol: 'Https' + sslCertificate: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/sslCertificates/${applicationGatewayFqdn}' + } + hostnames: [] + requireServerNameIndication: false + } + } + ] + location: location + managedIdentities: { + userAssignedResourceIds: [ + userAssignedIdentity.outputs.resourceId + ] + } + probes: (!empty(applicationGatewayPrimaryBackendEndFqdn)) + ? [ + { + name: 'webProbe' + properties: { + protocol: 'Https' + host: applicationGatewayPrimaryBackendEndFqdn + path: appGatewayBackendHealthProbePath + interval: 30 + timeout: 30 + unhealthyThreshold: 3 + pickHostNameFromBackendHttpSettings: false + minServers: 0 + match: { + statusCodes: [ + '200-499' + ] + } + } + } + ] + : null + requestRoutingRules: [ + { + name: 'routingRules' + properties: { + ruleType: 'Basic' + priority: 100 + httpListener: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/httpListeners/httpListener' + } + backendAddressPool: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/backendAddressPools/acaServiceBackend' + } + backendHttpSettings: { + #disable-next-line use-resource-id-functions + id: '${resourceId('Microsoft.Network/applicationGateways', naming.outputs.resourcesNames.applicationGateway)}/backendHttpSettingsCollection/https' + } + } + } + ] + sku: 'WAF_v2' + sslCertificates: (!empty(base64Certificate)) + ? [ + { + name: applicationGatewayFqdn + properties: { + keyVaultSecretId: appGatewayAddCertificates.outputs.SecretUri + } + } + ] + : [] + tags: tags + firewallPolicyId: appGwWafPolicy.outputs.resourceId + sslPolicyType: 'Predefined' + sslPolicyName: 'AppGwSslPolicy20220101' + zones: [ + '1' + '2' + '3' + ] + } +} + +module appGwWafPolicy 'br/public:avm/res/network/application-gateway-web-application-firewall-policy:0.1.0' = { + name: take('appGwWafPolicy-Deployment-${uniqueString(resourceGroup().id)}', 64) + params: { + name: '${naming.outputs.resourcesNames.applicationGateway}Policy001' + location: location + enableTelemetry: enableTelemetry + policySettings: { + fileUploadLimitInMb: 10 + state: 'Enabled' + mode: 'Prevention' + } + managedRules: { + managedRuleSets: [ + { + ruleSetType: 'OWASP' + ruleSetVersion: '3.2' + ruleGroupOverrides: [] + } + { + ruleSetType: 'Microsoft_BotManagerRuleSet' + ruleSetVersion: '0.1' + } + ] + } + tags: tags + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of the Azure Application Gateway.') +output applicationGatewayResourceId string = applicationGateway.outputs.resourceId + +@description('The FQDN of the Azure Application Gateway.') +output applicationGatewayFqdn string = applicationGatewayFqdn + +@description('The public IP address of the Azure Application Gateway.') +output applicationGatewayPublicIp string = applicationGatewayPublicIp.outputs.ipAddress diff --git a/avm/ptn/aca-lza/hosting-environment/modules/compute/linux-vm.bicep b/avm/ptn/aca-lza/hosting-environment/modules/compute/linux-vm.bicep new file mode 100644 index 0000000000..0446870dea --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/compute/linux-vm.bicep @@ -0,0 +1,175 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +param vmName string +param vmSize string +param storageAccountType string = 'Standard_LRS' +param vmZone int = 0 +param vmVnetName string +param vmSubnetName string +param vmSubnetAddressPrefix string +param vmNetworkSecurityGroupName string +param vmNetworkInterfaceName string +param logAnalyticsWorkspaceResourceId string +param bastionResourceId string +param vmAdminUsername string + +@secure() +param vmAdminPassword string + +@secure() +param vmSshPublicKey string + +@description('Type of authentication to use on the Virtual Machine. SSH key is recommended.') +@allowed([ + 'sshPublicKey' + 'password' +]) +param vmAuthenticationType string = 'password' + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +param location string = resourceGroup().location + +// ------------------ +// VARIABLES +// ------------------ + +// ------------------ +// RESOURCES +// ------------------ + +module vmNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.2.0' = { + name: 'vmNetworkSecurityDeployment' + params: { + name: vmNetworkSecurityGroupName + location: location + tags: tags + enableTelemetry: enableTelemetry + securityRules: !empty(bastionResourceId) + ? [ + { + name: 'allow-bastion-inbound' + properties: { + description: 'Allow inbound traffic from Bastion to the JumpBox' + protocol: '*' + sourceAddressPrefix: 'Bastion' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '*' + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + ] + : [] + } +} + +//TODO: Subnet deployment needs to be updated with AVM module once it is available +resource vmSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { + name: '${vmVnetName}/${vmSubnetName}' + properties: { + addressPrefix: vmSubnetAddressPrefix + networkSecurityGroup: { + id: vmNetworkSecurityGroup.outputs.resourceId + } + } +} +resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { + name: 'dep-mc-${vmName}' + location: location + properties: { + extensionProperties: { + InGuestPatchMode: 'User' + } + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + startDateTime: '2024-06-16 00:00' + duration: '03:55' + timeZone: 'W. Europe Standard Time' + recurEvery: '1Day' + } + visibility: 'Custom' + installPatches: { + rebootSetting: 'IfRequired' + linuxParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + } + } +} + +module vm 'br/public:avm/res/compute/virtual-machine:0.5.1' = { + name: 'vmDeployment' + params: { + name: vmName + location: location + tags: tags + enableTelemetry: enableTelemetry + osType: 'Linux' + computerName: vmName + adminUsername: vmAdminUsername + adminPassword: ((vmAuthenticationType == 'password') ? vmAdminPassword : null) + disablePasswordAuthentication: ((vmAuthenticationType == 'password') ? false : true) + encryptionAtHost: false + enableAutomaticUpdates: true + patchMode: 'AutomaticByPlatform' + bypassPlatformSafetyChecksOnUserSchedule: true + maintenanceConfigurationResourceId: maintenanceConfiguration.id + publicKeys: ((vmAuthenticationType == 'sshPublicKey') + ? [ + { + keyData: vmSshPublicKey + path: '/home/${vmAdminUsername}/.ssh/authorized_keys' + } + ] + : []) + nicConfigurations: [ + { + name: vmNetworkInterfaceName + enableAcceleratedNetworking: false + ipConfigurations: [ + { + name: 'ipConfig01' + privateIPAllocationMethod: 'Dynamic' + subnetResourceId: vmSubnet.id + } + ] + } + ] + osDisk: { + caching: 'ReadWrite' + createOption: 'FromImage' + deleteOption: 'Delete' + diskSizeGB: 128 + managedDisk: { + storageAccountType: storageAccountType + } + } + zone: vmZone + vmSize: vmSize + imageReference: { + publisher: 'canonical' + offer: '0001-com-ubuntu-server-focal' + sku: '20_04-lts-gen2' + version: 'latest' + } + extensionMonitoringAgentConfig: { + enabled: true + tags: tags + monitoringWorkspaceResourceId: logAnalyticsWorkspaceResourceId + } + } +} diff --git a/avm/ptn/aca-lza/hosting-environment/modules/compute/windows-vm.bicep b/avm/ptn/aca-lza/hosting-environment/modules/compute/windows-vm.bicep new file mode 100644 index 0000000000..166e668904 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/compute/windows-vm.bicep @@ -0,0 +1,158 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +param vmName string +param vmSize string +param storageAccountType string = 'Standard_LRS' +param vmWindowsOSVersion string = '2016-Datacenter' +param vmZone int = 0 +param vmVnetName string +param vmSubnetName string +param vmSubnetAddressPrefix string +param vmNetworkSecurityGroupName string +param vmNetworkInterfaceName string +param logAnalyticsWorkspaceResourceId string +param bastionResourceId string +param vmAdminUsername string + +@secure() +param vmAdminPassword string + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +param location string = resourceGroup().location + +// ------------------ +// VARIABLES +// ------------------ + +// ------------------ +// RESOURCES +// ------------------ + +module vmNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.2.0' = { + name: 'vmNetworkSecurityDeployment' + params: { + name: vmNetworkSecurityGroupName + location: location + tags: tags + enableTelemetry: enableTelemetry + securityRules: !empty(bastionResourceId) + ? [ + { + name: 'allow-bastion-inbound' + properties: { + description: 'Allow inbound traffic from Bastion to the JumpBox' + protocol: '*' + sourceAddressPrefix: 'Bastion' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '*' + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + ] + : [] + } +} + +//TODO: Subnet deployment needs to be updated with AVM module once it is available +resource vmSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { + name: '${vmVnetName}/${vmSubnetName}' + properties: { + addressPrefix: vmSubnetAddressPrefix + networkSecurityGroup: { + id: vmNetworkSecurityGroup.outputs.resourceId + } + } +} + +resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { + name: 'dep-mc-${vmName}' + location: location + properties: { + extensionProperties: { + InGuestPatchMode: 'User' + } + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + startDateTime: '2024-06-16 00:00' + duration: '03:55' + timeZone: 'W. Europe Standard Time' + recurEvery: '1Day' + } + visibility: 'Custom' + installPatches: { + rebootSetting: 'IfRequired' + windowsParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + } + } +} + +module vm 'br/public:avm/res/compute/virtual-machine:0.5.1' = { + name: 'vmDeployment' + params: { + name: vmName + location: location + tags: tags + enableTelemetry: enableTelemetry + osType: 'Windows' + computerName: vmName + adminUsername: vmAdminUsername + adminPassword: vmAdminPassword + encryptionAtHost: false + enableAutomaticUpdates: true + patchMode: 'AutomaticByPlatform' + bypassPlatformSafetyChecksOnUserSchedule: true + maintenanceConfigurationResourceId: maintenanceConfiguration.id + nicConfigurations: [ + { + name: vmNetworkInterfaceName + enableAcceleratedNetworking: false + ipConfigurations: [ + { + name: 'ipconfig1' + privateIPAllocationMethod: 'Dynamic' + subnetResourceId: vmSubnet.id + } + ] + } + ] + osDisk: { + caching: 'ReadWrite' + createOption: 'FromImage' + deleteOption: 'Delete' + diskSizeGB: 128 + managedDisk: { + storageAccountType: storageAccountType + } + } + zone: vmZone + vmSize: vmSize + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: vmWindowsOSVersion + version: 'latest' + } + extensionMonitoringAgentConfig: { + enabled: true + tags: tags + monitoringWorkspaceResourceId: logAnalyticsWorkspaceResourceId + } + } +} diff --git a/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/README.md b/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/README.md new file mode 100644 index 0000000000..27ece2b8c3 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/README.md @@ -0,0 +1,57 @@ +# Deploy the Azure Container Apps Environment + +With your spoke virtual network in place and the services that Azure Containers Apps needs in this architecture in place, you're ready to deploy the application platform. + +## Expected results + +The application platform, Azure Containers Apps, and its logging sinks within Azure Monitor will now be deployed. The workload is not deployed as part of step. Non-mission critical workload lifecycles are usually not tied to the lifecycle of the application platform, and as such are deployed isolated from infrastructure deployments, such as this one. Some cross-cutting concerns and platform feature enablement is usually handled however at this stage. + +![A picture of the resources of this architecture, now with the application platform.](./media/container-apps-environment.png) + +### Resources + +- Container Apps Environment Environment +- Log Analytics Workspace +- Application Insights (optional) +- Dapr Telemetry with Application Insights (optional) +- Private DNS Zone for Container Apps Environment + +## Steps + +1. Create the Azure Container Apps application platform resources. + + ```bash + RESOURCEID_VNET_HUB=$(az deployment sub show -n acalza01-hub --query properties.outputs.hubVNetId.value -o tsv) + RESOURCENAME_RESOURCEGROUP_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeResourceGroupName.value -o tsv) + RESOURCENAME_VNET_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeVNetName.value -o tsv) + LOG_ANALYTICS_WS_ID=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.logAnalyticsWorkspaceId.value -o tsv) + + echo RESOURCEID_VNET_HUB: $RESOURCEID_VNET_HUB && \ + echo RESOURCENAME_RESOURCEGROUP_SPOKE: $RESOURCENAME_RESOURCEGROUP_SPOKE && \ + echo RESOURCENAME_VNET_SPOKE: $RESOURCENAME_VNET_SPOKE && \ + echo LOG_ANALYTICS_WS_ID: $LOG_ANALYTICS_WS_ID + + # [This takes about 11 minutes to run.] + az deployment group create \ + -n acalza01-appplat \ + -g $RESOURCENAME_RESOURCEGROUP_SPOKE \ + -f 04-container-apps-environment/deploy.aca-environment.bicep \ + -p 04-container-apps-environment/deploy.aca-environment.parameters.jsonc \ + -p hubVNetId=${RESOURCEID_VNET_HUB} spokeVNetName=${RESOURCENAME_VNET_SPOKE} enableApplicationInsights=true enableDaprInstrumentation=true \ + -p logAnalyticsWorkspaceId=${LOG_ANALYTICS_WS_ID} + ``` + +1. Explore your final infrastructure. *Optional.* + + Now would be a good time to familiarize yourself with all core resources that are part of this architecture, as they are all deployed. This includes the networking layer, the application platform, and all supporting resources. It does not include any of the resources that are specific to a workload (such as public Internet ingress through an application gateway). Check out the following resource groups in the [Azure portal](https://portal.azure.com). + + ```bash + RESOURCENAME_RESOURCEGROUP_HUB=$(az deployment sub show -n acalza01-hub --query properties.outputs.resourceGroupName.value -o tsv) + + echo Hub Resource Group: $RESOURCENAME_RESOURCEGROUP_HUB && \ + echo Spoke Resource Group: $RESOURCENAME_RESOURCEGROUP_SPOKE + ``` + +## Next step + +:arrow_forward: [Deploy a sample application](../05-hello-world-sample-app/README.md) diff --git a/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/deploy.aca-environment.bicep b/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/deploy.aca-environment.bicep new file mode 100644 index 0000000000..b4f9a96c79 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/container-apps-environment/deploy.aca-environment.bicep @@ -0,0 +1,178 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('The location where the resources will be created. This needs to be the same region as the spoke.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +// Hub +@description('The resource ID of the existing hub virtual network.') +param hubVNetId string + +// Spoke +@description('The name of the existing spoke virtual network.') +param spokeVNetName string + +@description('The name of the existing spoke infrastructure subnet.') +param spokeInfraSubnetName string + +// Telemetry +@description('Enable or disable the createion of Application Insights.') +param enableApplicationInsights bool + +@description('Enable or disable Dapr application instrumentation using Application Insights. If enableApplicationInsights is false, this parameter is ignored.') +param enableDaprInstrumentation bool + +@description('The resource id of an existing Azure Log Analytics Workspace.') +param logAnalyticsWorkspaceId string + +@description('Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false.') +param deployZoneRedundantResources bool = true + +@description('The resource ID of the user-assigned managed identity for the Azure Container Registry to be able to pull images from it.') +param containerRegistryUserAssignedIdentityId string +// ------------------ +// VARIABLES +// ------------------ +var workProfileName = 'general-purpose' +var virtualNetworkLinks = concat( + [ + { + virtualNetworkResourceId: spokeVNet.id + registrationEnabled: false + } + ], + (!empty(hubVNetId)) + ? [ + { + virtualNetworkResourceId: hubVNetId + registrationEnabled: false + } + ] + : [] +) + +// ------------------ +// EXISTING RESOURCES +// ------------------ + +@description('The existing spoke virtual network.') +resource spokeVNet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: spokeVNetName + + resource infraSubnet 'subnets' existing = { + name: spokeInfraSubnetName + } +} + +// ------------------ +// RESOURCES +// ------------------ + +@description('User-configured naming rules') +module naming '../naming/naming.module.bicep' = { + name: take('acaNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(resourceGroup().id) + environment: environment + workloadName: workloadName + location: location + } +} + +@description('Azure Application Insights, the workload\' log & metric sink and APM tool') +module applicationInsights 'br/public:avm/res/insights/component:0.3.1' = if (enableApplicationInsights) { + name: take('applicationInsights-${uniqueString(resourceGroup().id)}', 64) + params: { + name: naming.outputs.resourcesNames.applicationInsights + location: location + tags: tags + enableTelemetry: enableTelemetry + workspaceResourceId: logAnalyticsWorkspaceId + } +} + +@description('The Azure Container Apps (ACA) cluster.') +module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.5.1' = { + name: take('acaenv-${uniqueString(resourceGroup().id)}', 64) + params: { + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceId + name: naming.outputs.resourcesNames.containerAppsEnvironment + location: location + tags: tags + enableTelemetry: enableTelemetry + internal: true + managedIdentities: { + userAssignedResourceIds: [ + containerRegistryUserAssignedIdentityId + ] + } + daprAIInstrumentationKey: (enableDaprInstrumentation && enableApplicationInsights) + ? applicationInsights.outputs.instrumentationKey + : null + infrastructureSubnetId: spokeVNet::infraSubnet.id + workloadProfiles: [ + { + maximumCount: 3 + minimumCount: 0 + name: workProfileName + workloadProfileType: 'D4' + } + ] + zoneRedundant: deployZoneRedundantResources + } +} + +@description('The Private DNS zone containing the ACA load balancer IP') +module containerAppsEnvironmentPrivateDnsZone 'br/public:avm/res/network/private-dns-zone:0.3.0' = { + name: 'acaDnsZoneDeployment-${uniqueString(resourceGroup().id)}' + params: { + name: containerAppsEnvironment.outputs.defaultDomain + location: 'global' + tags: tags + enableTelemetry: enableTelemetry + virtualNetworkLinks: virtualNetworkLinks + a: [ + { + name: '*' + aRecords: [ + { + ipv4Address: containerAppsEnvironment.outputs.staticIp + } + ] + } + ] + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of the Container Apps environment.') +output containerAppsEnvironmentId string = containerAppsEnvironment.outputs.resourceId + +@description('The name of the Container Apps environment.') +output containerAppsEnvironmentName string = containerAppsEnvironment.outputs.name + +@description('The name of the Application Insights instance.') +output applicationInsightsName string = (enableApplicationInsights) ? applicationInsights.outputs.name : '' + +@description('The name of the workload profiles provisioned in the Container Apps environment.') +output workloadProfileNames array = [workProfileName] diff --git a/avm/ptn/aca-lza/hosting-environment/modules/front-door/deploy.front-door.bicep b/avm/ptn/aca-lza/hosting-environment/modules/front-door/deploy.front-door.bicep new file mode 100644 index 0000000000..d6823451d4 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/front-door/deploy.front-door.bicep @@ -0,0 +1,197 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('The location where the resources will be created.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +// Container App Environment +@description('The ID of the Container Apps environment to be used for the deployment. (e.g. /subscriptions/XXX/resourceGroups/XXX/providers/Microsoft.App/managedEnvironments/XXX)') +param containerAppsEnvironmentId string + +// Private Link Service +@description('The resource ID of the subnet to be used for the private link service. (e.g. /subscriptions/XXX/resourceGroups/XXX/providers/Microsoft.Network/virtualNetworks/XXX/subnets/XXX)') +param privateLinkSubnetId string + +@description('The name of the front door endpoint to be created.') +param frontDoorEndpointName string = 'fde-containerapps' + +@description('The name of the front door origin group to be created.') +param frontDoorOriginGroupName string = 'containerapps-origin-group' + +@description('The name of the front door origin to be created.') +param frontDoorOriginName string = 'containerapps-origin' + +@description('The name of the front door origin route to be created.') +param frontDoorOriginRouteName string = 'containerapps-route' + +@description('The host name of the front door origin to be created.') +param frontDoorOriginHostName string + +// ------------------ +// VARIABLES +// ------------------ + +var containerAppsEnvironmentTokens = split(containerAppsEnvironmentId, '/') +var containerAppsEnvironmentSubscriptionId = containerAppsEnvironmentTokens[2] +var containerAppsEnvironmentResourceGroupName = containerAppsEnvironmentTokens[4] +var containerAppsEnvironmentName = containerAppsEnvironmentTokens[8] + +var privateLinkServiceName = '${naming.outputs.resourceTypeAbbreviations.privateLinkService}-${naming.outputs.resourcesNames.frontDoor}' + +// ------------------ +// RESOURCES +// ------------------ +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2024-03-01' existing = { + scope: resourceGroup(containerAppsEnvironmentSubscriptionId, containerAppsEnvironmentResourceGroupName) + name: containerAppsEnvironmentName +} + +module naming '../naming/naming.module.bicep' = { + name: take('frontDoorNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(resourceGroup().id) + environment: environment + workloadName: workloadName + location: location + } +} + +module privateLinkService 'private-link-service.bicep' = { + name: 'privateLinkServiceFrontDoorDeployment-${uniqueString(resourceGroup().id)}' + params: { + location: location + tags: tags + containerAppsDefaultDomainName: containerAppsEnvironment.properties.defaultDomain + containerAppsEnvironmentSubscriptionId: containerAppsEnvironmentSubscriptionId + privateLinkServiceName: privateLinkServiceName + privateLinkSubnetId: privateLinkSubnetId + } +} + +module frontDoor 'br/public:avm/res/cdn/profile:0.3.0' = { + name: 'frontDoorDeployment-${uniqueString(resourceGroup().id)}' + params: { + // Required parameters + name: 'dep-test-cdnpmax' + location: 'Global' + sku: 'Premium_AzureFrontDoor' + tags: tags + enableTelemetry: enableTelemetry + originResponseTimeoutSeconds: 60 + // Non-required parameters + endpointProperties: { + contentTypesToCompress: [ + 'application/javascript' + 'application/json' + 'application/x-javascript' + 'application/xml' + 'text/css' + 'text/html' + 'text/javascript' + 'text/plain' + ] + geoFilters: [] + isCompressionEnabled: true + isHttpAllowed: true + isHttpsAllowed: true + queryStringCachingBehavior: 'IgnoreQueryString' + } + originGroups: (!empty(frontDoorOriginHostName)) + ? [ + { + name: frontDoorOriginGroupName + loadBalancingSettings: { + sampleSize: 4 + successfulSamplesRequired: 3 + additionalLatencyInMilliseconds: 50 + } + healthProbeSettings: { + probePath: '/' + probeRequestType: 'HEAD' + probeProtocol: 'Https' + probeIntervalInSeconds: 100 + } + sessionAffinityState: 'Disabled' + origins: [ + { + name: frontDoorOriginName + hostName: frontDoorOriginHostName + httpPort: 80 + httpsPort: 443 + originHostHeader: frontDoorOriginHostName + priority: 1 + weight: 1000 + enabledState: 'Enabled' + sharedPrivateLinkResource: { + privateLink: { + id: privateLinkService.outputs.privateLinkServiceId + } + privateLinkLocation: location + requestMessage: 'frontdoor' + } + enforceCertificateNameCheck: true + } + ] + } + ] + : [] + afdEndpoints: [ + { + name: frontDoorEndpointName + originGroup: frontDoorOriginGroupName + enabledState: 'Enabled' + routes: [ + { + name: frontDoorOriginRouteName + originPath: '/' + ruleSets: [] + supportedProtocols: [ + 'Http' + 'Https' + ] + patternsToMatch: [ + '/*' + ] + forwardingProtocol: 'HttpsOnly' + linkToDefaultDomain: 'Enabled' + httpsRedirect: 'Enabled' + enabledState: 'Enabled' + } + ] + } + ] + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The ID of the front door resource.') +output frontDoorResourceId string = frontDoor.outputs.resourceId +@description('The ID of the private link service.') +output privateLinkServiceId string = privateLinkService.outputs.privateLinkServiceId +@description('The ID of the private link endpoint connection to approve.') +output privateLinkEndpointConnectionId string = length(privateLinkService.outputs.privateEndpointConnections) > 0 + ? filter( + privateLinkService.outputs.privateEndpointConnections, + (connection) => connection.properties.privateLinkServiceConnectionState.description == 'frontdoor' + )[0].id + : '' diff --git a/avm/ptn/aca-lza/hosting-environment/modules/front-door/private-link-service.bicep b/avm/ptn/aca-lza/hosting-environment/modules/front-door/private-link-service.bicep new file mode 100644 index 0000000000..41dfe7c165 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/front-door/private-link-service.bicep @@ -0,0 +1,72 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The location where the resources will be created.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('The name of the private link service to create.') +param privateLinkServiceName string + +@description('SubnetId used to configure private link service.') +param privateLinkSubnetId string + +@description('Azure Container Apps Environment Default Domain Name') +param containerAppsDefaultDomainName string + +@description('Azure Container Apps Environment Subscription Id') +param containerAppsEnvironmentSubscriptionId string + +// ------------------ +// VARIABLES +// ------------------ + +// => Resolve container apps environment managed resource group name to get the frontend Ip configuration +var containerAppsDefaultDomainArray = split(containerAppsDefaultDomainName, '.') +var containerAppsNameIdentifier = containerAppsDefaultDomainArray[lastIndexOf(containerAppsDefaultDomainArray, location) - 1] +var containerAppsManagedResourceGroup = 'MC_${containerAppsNameIdentifier}-rg_${containerAppsNameIdentifier}_${location}' + +// ------------------ +// RESOURCES +// ------------------ + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-05-01' existing = { + name: 'kubernetes-internal' + scope: resourceGroup(containerAppsEnvironmentSubscriptionId, containerAppsManagedResourceGroup) +} + +// CANOT USE MODULE HERE BECAUSE OF THE LOADBALANCER RUNTIME FRONTEND IP CONFIGURATION RETRIEVAL +resource privateLinkService 'Microsoft.Network/privateLinkServices@2023-11-01' = { + name: privateLinkServiceName + location: location + tags: tags + properties: { + loadBalancerFrontendIpConfigurations: [ + { + id: loadBalancer.properties.frontendIPConfigurations[0].id + } + ] + ipConfigurations: [ + { + name: 'snet-provider-default-1' + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: privateLinkSubnetId + } + primary: true + privateIPAddressVersion: 'IPv4' + } + } + ] + } +} + +output privateLinkServiceId string = privateLinkService.id + +output privateEndpointConnections array = privateLinkService.properties.privateEndpointConnections diff --git a/avm/ptn/aca-lza/hosting-environment/modules/naming/naming-rules.jsonc b/avm/ptn/aca-lza/hosting-environment/modules/naming/naming-rules.jsonc new file mode 100644 index 0000000000..2a1c4d0112 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/naming/naming-rules.jsonc @@ -0,0 +1,90 @@ +{ + // Recommended abreviations: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations + "resourceTypeAbbreviations" : { + "applicationGateway": "agw", + "applicationInsights": "appi", + "appService": "app", + "azureFirewall": "azfw", + "bastion": "bas", + "containerAppsEnvironment": "cae", + "containerRegistry": "cr", + "cosmosDbNoSql": "cosno", + "frontDoor": "afd", + "frontDoorEndpoint": "fde", + "frontDoorWaf": "fdfp", + "keyVault": "kv", + "logAnalyticsWorkspace": "log", + "managedIdentity": "id", + "networkInterface": "nic", + "networkSecurityGroup": "nsg", + "privateEndpoint": "pep", + "privateLinkService": "pls", + "publicIpAddress": "pip", + "resourceGroup": "rg", + "routeTable": "rt", + "serviceBus": "sb", + "serviceBusQueue": "sbq", + "serviceBusTopic": "sbt", + "storageAccount": "st", + "virtualMachine": "vm", + "virtualNetwork": "vnet", + "redisCache": "redis", + "cognitiveAccount": "cog", + "openAiDeployment": "oaidep", + "azureAISearch": "srch", + "documentIntelligence": "di", + "eventGridSystemTopic": "egst", + "eventGridSubscription": "evgs" + }, + + //copied from here: https://github.com/nianton/azure-naming/blob/main/datafiles/regionAbbreviations.json + "regionAbbreviations" : { + "australiacentral": "auc", + "australiacentral2": "auc2", + "australiaeast": "aue", + "australiasoutheast": "ause", + "brazilsouth": "brs", + "brazilsoutheast": "brse", + "canadacentral": "canc", + "canadaeast": "cane", + "centralindia": "cin", + "centralus": "cus", + "centraluseuap": "cuseuap", + "eastasia": "ea", + "eastus": "eus", + "eastus2": "eus2", + "eastus2euap": "eus2euap", + "francecentral": "frc", + "francesouth": "frs", + "germanynorth": "gern", + "germanywestcentral": "gerwc", + "japaneast": "jae", + "japanwest": "jaw", + "jioindiacentral": "jioinc", + "jioindiawest": "jioinw", + "koreacentral": "koc", + "koreasouth": "kors", + "northcentralus": "ncus", + "northeurope": "neu", + "norwayeast": "nore", + "norwaywest": "norw", + "southafricanorth": "san", + "southafricawest": "saw", + "southcentralus": "scus", + "southeastasia": "sea", + "southindia": "sin", + "swedencentral": "swc", + "switzerlandnorth": "swn", + "switzerlandwest": "sww", + "uaecentral": "uaec", + "uaenorth": "uaen", + "uksouth": "uks", + "ukwest": "ukw", + "westcentralus": "wcus", + "westeurope": "weu", + "westindia": "win", + "westus": "wus", + "westus2": "wus2", + "westus3": "wus3" + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/naming/naming.module.bicep b/avm/ptn/aca-lza/hosting-environment/modules/naming/naming.module.bicep new file mode 100644 index 0000000000..386456b923 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/naming/naming.module.bicep @@ -0,0 +1,117 @@ +// ------------------ +// PARAMETERS +// ------------------ +@minLength(2) +@maxLength(10) +@description('The name of the workloard that is being deployed. Up to 10 characters long.') +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa") Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('Location for all Resources.') +param location string + +@description('a unique ID that can be appended (or prepended) in azure resource names that require some kind of uniqueness') +param uniqueId string + +// ------------------ +// VARIABLES +// ------------------ + +var naming = json(loadTextContent('./naming-rules.jsonc')) + +// get arbitary 5 first characters (instead of something like 5yj4yjf5mbg72), to save string length. +var uniqueIdShort = substring(uniqueId, 0, 5) +var resourceTypeToken = 'RES_TYPE' + +// Define and adhere to a naming convention, such as: https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming +var namingBase = '${resourceTypeToken}-${workloadName}-${environment}-${naming.regionAbbreviations[toLower(location)]}' +var namingBaseUnique = '${resourceTypeToken}-${workloadName}-${uniqueIdShort}-${environment}-${naming.regionAbbreviations[toLower(location)]}' + +// Used for hub resources - should be shared across different workloads +var namingBaseNoWorkloadName = '${resourceTypeToken}-${environment}-${naming.regionAbbreviations[toLower(location)]}' + +var resourceTypeAbbreviations = naming.resourceTypeAbbreviations + +var keyVaultName = take(replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.keyVault), 24) + +var resourceNames = { + vnetSpoke: '${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.virtualNetwork)}-spoke' + vnetHub: '${replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.virtualNetwork)}-hub' + applicationGateway: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.applicationGateway) + applicationGatewayPip: '${naming.resourceTypeAbbreviations.publicIpAddress}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.applicationGateway)}' + applicationGatewayUserAssignedIdentity: '${naming.resourceTypeAbbreviations.managedIdentity}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.applicationGateway)}-KeyVaultSecretUser' + applicationGatewayNsg: '${naming.resourceTypeAbbreviations.networkSecurityGroup}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.applicationGateway)}' + pepNsg: '${naming.resourceTypeAbbreviations.networkSecurityGroup}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.privateEndpoint)}' + applicationInsights: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.applicationInsights) + azureFirewall: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.azureFirewall) + azureFirewallPip: '${naming.resourceTypeAbbreviations.publicIpAddress}-${replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.azureFirewall)}' + bastion: replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.bastion) + bastionNsg: '${naming.resourceTypeAbbreviations.networkSecurityGroup}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.bastion)}' + bastionPip: '${naming.resourceTypeAbbreviations.publicIpAddress}-${replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.bastion)}' + containerAppsEnvironment: replace( + namingBase, + resourceTypeToken, + naming.resourceTypeAbbreviations.containerAppsEnvironment + ) + containerAppsEnvironmentNsg: '${naming.resourceTypeAbbreviations.networkSecurityGroup}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.containerAppsEnvironment)}' + containerRegistry: take( + toLower(replace( + replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.containerRegistry), + '-', + '' + )), + 50 + ) + containerRegistryPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${toLower( replace ( replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.containerRegistry), '-', '' ) )}' + containerRegistryUserAssignedIdentity: '${naming.resourceTypeAbbreviations.managedIdentity}-${toLower( replace ( replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.containerRegistry), '-', '' ) )}-AcrPull' + redisCache: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.redisCache) + redisCachePep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace ( namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.redisCache )}' + openAiAccount: replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.cognitiveAccount) + openAiDeployment: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.openAiDeployment) + cosmosDbNoSql: toLower(take( + replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.cosmosDbNoSql), + 44 + )) + cosmosDbNoSqlPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${toLower( take(replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.cosmosDbNoSql), 44) )}' + frontDoorProfile: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.frontDoor) + keyVault: endsWith(keyVaultName, '-') ? take(keyVaultName, 23) : keyVaultName + keyVaultPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace ( namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.keyVault )}' + logAnalyticsWorkspace: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.logAnalyticsWorkspace) + routeTable: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.routeTable) + serviceBus: replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.serviceBus) + serviceBusPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.serviceBus)}' + storageAccount: toLower(take( + replace(replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.storageAccount), '-', ''), + 24 + )) + storageAccountPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${toLower( replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.storageAccount))}' + vmJumpBox: replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.virtualMachine) + vmJumpBoxNsg: '${naming.resourceTypeAbbreviations.networkSecurityGroup}-${replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.virtualMachine)}' + vmJumpBoxNic: '${naming.resourceTypeAbbreviations.networkInterface}-${replace(namingBaseNoWorkloadName, resourceTypeToken, naming.resourceTypeAbbreviations.virtualMachine)}' + frontDoor: replace(namingBase, resourceTypeToken, naming.resourceTypeAbbreviations.frontDoor) + azureAISearch: replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.azureAISearch) + azureAISearchPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace ( namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.azureAISearch )}' + documentIntelligence: replace( + namingBaseUnique, + resourceTypeToken, + naming.resourceTypeAbbreviations.documentIntelligence + ) + documentIntelligencePep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace ( namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.documentIntelligence )}' + eventGridSystemTopic: replace( + namingBaseUnique, + resourceTypeToken, + naming.resourceTypeAbbreviations.eventGridSystemTopic + ) + eventGridSystemTopicPep: '${naming.resourceTypeAbbreviations.privateEndpoint}-${replace(namingBaseUnique, resourceTypeToken, naming.resourceTypeAbbreviations.eventGridSystemTopic)}' + eventGridSubscription: replace( + namingBaseUnique, + resourceTypeToken, + naming.resourceTypeAbbreviations.eventGridSubscription + ) +} + +output resourcesNames object = resourceNames +output resourceTypeAbbreviations object = resourceTypeAbbreviations diff --git a/avm/ptn/aca-lza/hosting-environment/modules/networking/peering.bicep b/avm/ptn/aca-lza/hosting-environment/modules/networking/peering.bicep new file mode 100644 index 0000000000..4fd0a55013 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/networking/peering.bicep @@ -0,0 +1,24 @@ +@description('The name of the local Virtual Network') +param localVnetName string + +@description('The name of the remote Virtual Network') +param remoteVnetName string + +@description('The name of the resource group of the remote virtual netowrk') +param remoteRgName string + +@description('The id of the subscription of the remote virtual netowrk') +param remoteSubscriptionId string + +resource vnetPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-08-01' = { + name: '${localVnetName}/peerTo-${remoteVnetName}' + properties: { + allowVirtualNetworkAccess: true + allowGatewayTransit: false + allowForwardedTraffic: false + useRemoteGateways: false + remoteVirtualNetwork: { + id: resourceId(remoteSubscriptionId, remoteRgName, 'Microsoft.Network/virtualNetworks', remoteVnetName) + } + } +} diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policies-assignement.bicep b/avm/ptn/aca-lza/hosting-environment/modules/policy/policies-assignement.bicep new file mode 100644 index 0000000000..2c851cc735 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policies-assignement.bicep @@ -0,0 +1,268 @@ +targetScope = 'managementGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The location where the resources will be created.') +param location string + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +@description('The name of the resource group where the resources will be created.') +param spokeResourceGroupName string + +@description('The name of the Container Registry that will be allow-listed by the policy.') +param containerRegistryName string + +// ------------------ +// VARIABLES +// ------------------ +// Azure Container Apps Built-in Policy Definitions: https://learn.microsoft.com/azure/container-apps/policy-reference#policy-definitions +var builtInPolicies = [ + { + name: 'authentication-should-be-enabled-on-container-apps' + definition: { + properties: { + displayName: 'Authentication should be enabled on container apps' + description: 'Container Apps Authentication is a feature that can prevent anonymous HTTP requests from reaching the Container App, or authenticate those that have tokens before they reach the Container App' + } + } + parameters: { + effect: { + value: 'AuditIfNotExists' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/2b585559-a78e-4cc4-b1aa-fb169d2f6b96' + } + { + name: 'container-app-environments-should-use-network-injection' + definition: { + properties: { + displayName: 'Container App environments should use network injection' + description: 'Container Apps environments should use virtual network injection to: 1.Isolate Container Apps from the public internet 2.Enable network integration with resources on-premises or in other Azure virtual networks 3.Achieve more granular control over network traffic flowing to and from the environment' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/8b346db6-85af-419b-8557-92cee2c0f9bb' + } + { + name: 'container-app-should-configure-with-volume-mount' + definition: { + properties: { + displayName: 'Container App should configure with volume mount' + description: 'Enforce the use of volume mounts for Container Apps to ensure availability of persistent storage capacity' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/7c9f3fbb-739d-4844-8e42-97e3be6450e0' + } + { + name: 'container-app-should-disable-public-network-access' + definition: { + properties: { + displayName: 'Container Apps environment should disable public network access' + description: 'Disable public network access to improve security by exposing the Container Apps environment through an internal load balancer. This removes the need for a public IP address and prevents internet access to all Container Apps within the environment.' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/d074ddf8-01a5-4b5e-a2b8-964aed452c0a' + } + { + name: 'container-apps-should-disable-external-network-access' + definition: { + properties: { + displayName: 'Container Apps should disable external network access' + description: 'Disable external network access to your Container Apps by enforcing internal-only ingress. This will ensure inbound communication for Container Apps is limited to callers within the Container Apps environment' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/783ea2a8-b8fd-46be-896a-9ae79643a0b1' + } + { + name: 'containerapps-should-only-be-accessible-over-HTTPS' + definition: { + properties: { + displayName: 'Container Apps should only be accessible over HTTPS' + description: 'Use of HTTPS ensures server/service authentication and protects data in transit from network layer eavesdropping attacks. Disabling "allowInsecur" will result in the automatic redirection of requests from HTTP to HTTPS connections for container apps.' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/0e80e269-43a4-4ae9-b5bc-178126b8a5cb' + } + { + name: 'managed-identity-should-be-enabled' + definition: { + properties: { + displayName: 'Managed Identity should be enabled for Container Apps' + description: 'Enforcing managed identity ensures Container Apps can securely authenticate to any resource that supports Azure AD authentication' + } + } + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/b874ab2d-72dd-47f1-8cb5-4a306478a4e7' + } +] + +// credits: https://techcommunity.microsoft.com/t5/fasttrack-for-azure/azure-policy-for-azure-container-apps-yes-please/ba-p/3775200 +// https://github.com/Azure-Samples/aca-azure-policy/tree/main +var customPolicies = [ + { + name: 'aca-allowed-container-registries' + definition: json(loadTextContent('policy-definitions/aca-allowed-container-registries.json')) + parameters: { + listOfAllowedContainerRegistries: { + value: [ + 'mcr.microsoft.com' + 'docker.io' + 'ghcr.io' + '${containerRegistryName}.azurecr.io' + ] + } + effect: { + value: 'Audit' + } + } + } + { + name: 'aca-replica-count' + definition: json(loadTextContent('policy-definitions/aca-replica-count.json')) + parameters: { + minReplicas: { + value: 0 + } + maxReplicas: { + value: 30 + } + effect: { + value: 'Audit' + } + } + identity: false + } + { + name: 'aca-no-liveness-probes' + definition: json(loadTextContent('policy-definitions/aca-no-liveness-probes.json')) + parameters: { + effect: { + value: 'Audit' + } + } + identity: false + } + { + name: 'aca-no-readiness-probes' + definition: json(loadTextContent('policy-definitions/aca-no-readiness-probes.json')) + parameters: { + effect: { + value: 'Audit' + } + } + identity: false + } + { + name: 'aca-no-startup-probes' + definition: json(loadTextContent('policy-definitions/aca-no-startup-probes.json')) + parameters: { + effect: { + value: 'Audit' + } + } + identity: false + } + { + name: 'aca-required-cpu-and-memory' + definition: json(loadTextContent('policy-definitions/aca-required-cpu-and-memory.json')) + parameters: { + maxCpu: { + value: '1.0' + } + maxMemory: { + value: '2.5' + } + effect: { + value: 'Audit' + } + } + identity: false + } + { + name: 'aca-no-monitoring' + definition: json(loadTextContent('policy-definitions/aca-no-monitoring.json')) + parameters: { + effect: { + value: 'Audit' + } + } + identity: false + } +] + +module builtInPolicyAssignment 'br/public:avm/ptn/authorization/policy-assignment:0.1.0' = [ + for (policy, i) in builtInPolicies: { + name: 'poAssign_${take(policy.name, 40)}' + params: { + name: policy.name + location: location + enableTelemetry: enableTelemetry + policyDefinitionId: policy.policyDefinitionId + resourceGroupName: spokeResourceGroupName + } + } +] + +//TODO: Needs to be updated when the AVM is implemented +resource customPoliciesDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = [ + for policy in customPolicies: { + name: guid(policy.name) + properties: { + description: policy.definition.properties.description + displayName: policy.definition.properties.displayName + metadata: policy.definition.properties.metadata + mode: policy.definition.properties.mode + parameters: policy.definition.properties.parameters + policyType: policy.definition.properties.policyType + policyRule: policy.definition.properties.policyRule + } + } +] + +module cusomPoliciesAssignement 'br/public:avm/ptn/authorization/policy-assignment:0.1.0' = [ + for (policy, i) in customPolicies: { + name: 'poAssign_${take(policy.name, 40)}' + params: { + name: policy.name + location: location + enableTelemetry: enableTelemetry + policyDefinitionId: customPoliciesDefinition[i].id + resourceGroupName: spokeResourceGroupName + } + dependsOn: [ + customPoliciesDefinition + ] + } +] diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-container-registries.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-container-registries.json new file mode 100644 index 0000000000..9852ed681d --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-container-registries.json @@ -0,0 +1,59 @@ +{ + "properties": { + "displayName": "Azure Container Apps allowed container registries", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to restrict the list of container registries for Azure Container Apps.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "listOfAllowedContainerRegistries": { + "type": "Array", + "metadata": { + "displayName": "Allowed container registries", + "description": "The list of container registries that can be specified when deploying resources." + }, + "defaultValue": [ + "mcr.microsoft.com" + ] + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "count": { + "field": "Microsoft.App/containerApps/template.containers[*]", + "where": { + "value": "[split(first(field('Microsoft.App/containerApps/template.containers[*].image')), '/')[0]]", + "notIn": "[parameters('listOfAllowedContainerRegistries')]" + } + }, + "greater": 0 + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-target-ports.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-target-ports.json new file mode 100644 index 0000000000..23779be261 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-target-ports.json @@ -0,0 +1,64 @@ +{ + "properties": { + "displayName": "Azure Container Apps allowed ingress target ports", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to restrict the list of ingress target ports for Azure Container Apps.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "listOfAllowedIngressTargetPorts": { + "type": "Array", + "metadata": { + "displayName": "Allowed container ports", + "description": "The list of container ports that can be specified when deploying resources." + }, + "defaultValue": [ + 80, + 443 + ] + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "field": "Microsoft.App/containerApps/configuration.ingress", + "exists": true + }, + { + "not": { + "field": "Microsoft.App/containerApps/configuration.ingress", + "equals": "" + } + }, + { + "field": "Microsoft.App/containerApps/configuration.ingress.targetPort", + "notIn": "[parameters('listOfAllowedIngressTargetPorts')]" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-transports.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-transports.json new file mode 100644 index 0000000000..6bf941b970 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-ingress-transports.json @@ -0,0 +1,64 @@ +{ + "properties": { + "displayName": "Azure Container Apps allowed ingress transports", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to restrict the list of ingress transports for Azure Container Apps.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "listOfAllowedIngressTransports": { + "type": "Array", + "metadata": { + "displayName": "Allowed container transports", + "description": "The list of container transports that can be specified when deploying resources." + }, + "defaultValue": [ + 80, + 443 + ] + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "field": "Microsoft.App/containerApps/configuration.ingress", + "exists": true + }, + { + "not": { + "field": "Microsoft.App/containerApps/configuration.ingress", + "equals": "" + } + }, + { + "field": "Microsoft.App/containerApps/configuration.ingress.transport", + "notIn": "[parameters('listOfAllowedIngressTransports')]" + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-locations.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-locations.json new file mode 100644 index 0000000000..f19f2ac164 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-allowed-locations.json @@ -0,0 +1,67 @@ +{ + "properties": { + "displayName": "Azure Container Apps allowed locations", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to restrict the locations for Azure Container Apps.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "listOfAllowedLocations": { + "type": "Array", + "metadata": { + "displayName": "Allowed locations", + "description": "The list of locations that can be specified when deploying resources.", + "strongType": "location" + }, + "defaultValue": [ + "northeurope", + "westeurope", + "eastus2" + ] + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "location", + "notIn": "[parameters('listOfAllowedLocations')]" + }, + { + "field": "location", + "notEquals": "global" + }, + { + "anyOf": [{ + "field": "type", + "equals": "Microsoft.App/managedEnvironments" + }, + { + "field": "type", + "equals": "Microsoft.App/containerApps" + } + ] + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-diagnostic-settings.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-diagnostic-settings.json new file mode 100644 index 0000000000..7753edc507 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-diagnostic-settings.json @@ -0,0 +1,78 @@ +{ + "properties": { + "displayName": "Azure Container Apps no diagnostic settings configured", + "policyType": "Custom", + "mode": "All", + "description": "When the environment configure to collect logs and metrics to Azure Monitor, checks whether diagnostic settings have been defined.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "logsEnabled": { + "type": "Boolean", + "metadata": { + "displayName": "Logs Enabled" + }, + "allowedValues": [ + true, + false + ], + "defaultValue": true + }, + "metricsEnabled": { + "type": "Boolean", + "metadata": { + "displayName": "Metrics Enabled" + }, + "allowedValues": [ + true, + false + ], + "defaultValue": true + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "AuditIfNotExists", + "Disabled" + ], + "defaultValue": "AuditIfNotExists" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/managedEnvironments" + }, + { + "field": "Microsoft.App/managedEnvironments/appLogsConfiguration.destination", + "equals": "azure-monitor" + } + ] + }, + "then": { + "effect": "[parameters('effect')]", + "details": { + "type": "Microsoft.Insights/diagnosticSettings", + "existenceCondition": { + "allOf": [{ + "field": "Microsoft.Insights/diagnosticSettings/logs.enabled", + "equals": "[parameters('logsEnabled')]" + }, + { + "field": "Microsoft.Insights/diagnosticSettings/metrics.enabled", + "equals": "[parameters('metricsEnabled')]" + } + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-liveness-probes.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-liveness-probes.json new file mode 100644 index 0000000000..2525cdef64 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-liveness-probes.json @@ -0,0 +1,49 @@ +{ + "properties": { + "displayName": "Azure Container Apps no container liveness probes", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enforces that all the containers of Azure Containers Apps have liveness probes configured.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "count": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*]", + "where": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*].type", + "equals": "Liveness" + } + }, + "equals": 0 + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-monitoring.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-monitoring.json new file mode 100644 index 0000000000..2dff563b37 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-monitoring.json @@ -0,0 +1,43 @@ +{ + "properties": { + "displayName": "Azure Container Apps no monitoring configured", + "policyType": "Custom", + "mode": "All", + "description": "This policy enforces that monitoring is configured for Azure Container Apps environments.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/managedEnvironments" + }, + { + "field": "Microsoft.App/managedEnvironments/appLogsConfiguration.destination", + "exists": false + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-readiness-probes.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-readiness-probes.json new file mode 100644 index 0000000000..995a872d4c --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-readiness-probes.json @@ -0,0 +1,49 @@ +{ + "properties": { + "displayName": "Azure Container Apps no container readiness probes", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enforces that all the containers of Azure Containers Apps have readiness probes configured.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "count": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*]", + "where": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*].type", + "equals": "Readiness" + } + }, + "equals": 0 + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-startup-probes.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-startup-probes.json new file mode 100644 index 0000000000..6d3a7c1fa2 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-no-startup-probes.json @@ -0,0 +1,49 @@ +{ + "properties": { + "displayName": "Azure Container Apps no container startup probes", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enforces that all the containers of Azure Containers Apps have startup probes configured.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "count": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*]", + "where": { + "field": "Microsoft.App/containerApps/template.containers[*].probes[*].type", + "equals": "Startup" + } + }, + "equals": 0 + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-replica-count.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-replica-count.json new file mode 100644 index 0000000000..78accd44b2 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-replica-count.json @@ -0,0 +1,64 @@ +{ + "properties": { + "displayName": "Azure Container Apps container replica count limits", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enforces limits for the minimum and maximum number of replicas for Azure Container Apps.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "minReplicas": { + "type": "integer", + "metadata": { + "displayName": "Min allowed replicas", + "description": "Specifies the minimum number of container replicas for the Azure Container App" + }, + "defaultValue": 0 + }, + "maxReplicas": { + "type": "integer", + "metadata": { + "displayName": "Max allowed replicas", + "description": "Specifies the maximum number of container replicas for the Azure Container App" + }, + "defaultValue": 30 + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "anyOf": [{ + "field": "Microsoft.App/containerApps/template.scale.minReplicas", + "less": "[parameters('MinReplicas')]" + }, { + "field": "Microsoft.App/containerApps/template.scale.maxReplicas", + "greater": "[parameters('MaxReplicas')]" + }] + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-required-cpu-and-memory.json b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-required-cpu-and-memory.json new file mode 100644 index 0000000000..bb0e8451d8 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/policy/policy-definitions/aca-required-cpu-and-memory.json @@ -0,0 +1,78 @@ +{ + "properties": { + "displayName": "Azure Container Apps container required CPU and memory", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enforces limits for container CPU and memory requests in an AZure Container App.", + "metadata": { + "version": "1.0.0", + "category": "Azure Container Apps" + }, + "parameters": { + "maxCpu": { + "type": "String", + "metadata": { + "displayName": "Max allowed CPU cores", + "description": "Specifies the maximum CPU cores allowed for a container. E.g. 1.25." + }, + "defaultValue": "2.0" + }, + "maxMemory": { + "type": "String", + "metadata": { + "displayName": "Max allowed memory in Gi", + "description": "Specifies the maximum memory in Gi allowed for a container. E.g. 2.5" + }, + "defaultValue": "4.0" + }, + "effect": { + "type": "String", + "metadata": { + "displayName": "Effect", + "description": "Enable or disable the execution of the policy" + }, + "allowedValues": [ + "Audit", + "Deny", + "Disabled" + ], + "defaultValue": "Audit" + } + }, + "policyRule": { + "if": { + "allOf": [{ + "field": "type", + "equals": "Microsoft.App/containerApps" + }, + { + "anyOf": [{ + "count": { + "field": "Microsoft.App/containerApps/template.containers[*]", + "where": { + "field": "Microsoft.App/containerApps/template.containers[*].resources.cpu", + "greater": "[float(parameters('maxCpu'))]" + } + }, + "greater": 0 + }, + { + "count": { + "field": "Microsoft.App/containerApps/template.containers[*]", + "where": { + "value": "[float(substring(first(field('Microsoft.App/containerApps/template.containers[*].resources.memory')), 0, sub(length(first(field('Microsoft.App/containerApps/template.containers[*].resources.memory'))), 2)))]", + "greater": "[float(parameters('maxMemory'))]" + } + }, + "greater": 0 + } + ] + } + ] + }, + "then": { + "effect": "[parameters('effect')]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/aca-lza/hosting-environment/modules/sample-application/deploy.sample-application.bicep b/avm/ptn/aca-lza/hosting-environment/modules/sample-application/deploy.sample-application.bicep new file mode 100644 index 0000000000..b6fd0b1b1c --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/sample-application/deploy.sample-application.bicep @@ -0,0 +1,73 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The location where the resources will be created. This needs to be the same region as the Azure Container Apps instances.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +@description('Optional. The name of the Container App. If set, it overrides the name generated by the template.') +@minLength(2) +@maxLength(32) +param helloWorldContainerAppName string = 'ca-simple-hello' + +@description('The resource ID of the existing user-assigned managed identity to be assigned to the Container App to be able to pull images from the container registry.') +param containerRegistryUserAssignedIdentityId string + +@description('The resource ID of the existing Container Apps environment in which the Container App will be deployed.') +param containerAppsEnvironmentId string + +@description('The container apps environment workload profile to use for the Container App.') +param workloadProfileName string + +// ------------------ +// RESOURCES +// ------------------ + +module sampleApplication 'br/public:avm/res/app/container-app:0.4.0' = { + name: helloWorldContainerAppName + params: { + name: helloWorldContainerAppName + location: location + tags: tags + enableTelemetry: enableTelemetry + environmentId: containerAppsEnvironmentId + managedIdentities: { + userAssignedResourceIds: [ + containerRegistryUserAssignedIdentityId + ] + } + workloadProfileName: workloadProfileName + containers: [ + { + name: 'simple-hello' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + resources: { + cpu: json('0.25') + memory: '0.5Gi' + } + } + ] + scaleMinReplicas: 2 + scaleMaxReplicas: 10 + activeRevisionsMode: 'Single' + ingressExternal: true + ingressAllowInsecure: false + ingressTargetPort: 80 + ingressTransport: 'auto' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The FQDN of the "Hello World" Container App.') +output helloWorldAppFqdn string = sampleApplication.outputs.fqdn diff --git a/avm/ptn/aca-lza/hosting-environment/modules/spoke/README.md b/avm/ptn/aca-lza/hosting-environment/modules/spoke/README.md new file mode 100644 index 0000000000..f472066c43 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/spoke/README.md @@ -0,0 +1,53 @@ +# Deploy the spoke network + + +## Networking in this architecture + +The regional spoke network in which your application platform is laid into acts as the first line of defense for your workload. This network perimeter forms a security boundary where you will restrict the network line of sight into your resources. It also gives your application platform the ability to use private link to talk to adjacent platform-as-a-service resources such as Key Vault and Azure Container Registry. And finally it acts as a layer to restrict and tunnel egressing traffic. All of this adds up to ensure that workload traffic stays as isolated as possible and free from any possible external influence, including other enterprise workloads. + +## Expected results + +After executing these steps you'll have the spoke resource group (`rg-lzaaca-spoke-dev-reg`, by default) populated with a virtual network, subnets, and peering to the regional hub. Based on how you configured the naming and deployment parameters, your result may be slightly different. + +### Resources + +- Spoke resource group +- Spoke virtual network +- Peering to and from the hub (optional) +- Jump box virtual machine (optional) + +## Steps + +1. Create the regional spoke network. + + ```bash + RESOURCEID_VNET_HUB=$(az deployment sub show -n acalza01-hub --query properties.outputs.hubVNetId.value -o tsv) + echo RESOURCEID_VNET_HUB: $RESOURCEID_VNET_HUB + + # [This takes about two minutes to run.] + az deployment sub create \ + -n acalza01-spokenetwork \ + -l $LOCATION \ + -f 02-spoke/deploy.spoke.bicep -p 02-spoke/deploy.spoke.parameters.jsonc \ + -p hubVNetId=${RESOURCEID_VNET_HUB} + ``` + +1. Explore your networking resources. *Optional.* + + You may wish to take this moment to familiarize yourself with the resources that have been deployed so far to Azure. They have all been networking resources, establishing the network and access boundaries from within which your application platform will be executing. Check out the following resource groups in the [Azure portal](https://portal.azure.com). + + ```bash + RESOURCENAME_RESOURCEGROUP_HUB=$(az deployment sub show -n acalza01-hub --query properties.outputs.resourceGroupName.value -o tsv) + RESOURCENAME_RESOURCEGROUP_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeResourceGroupName.value -o tsv) + + echo Hub Resource Group: $RESOURCENAME_RESOURCEGROUP_HUB && \ + echo Spoke Resource Group: $RESOURCENAME_RESOURCEGROUP_SPOKE + ``` + +## Azure landing zone platform alignment + +The creation of the hub resources, spoke virtual network, and routing configuration are usually the responsibility of the connectivity platform team. While the creation of subnets, NSGs, and the workload resources are delegated to the workload team. The deployment steps so far have been a mix of both roles. Be sure to understand your organization's separation of duties in landing zone deployments, and use your organization's subscription vending solution to . From this point on in the walkthrough, the steps are indeed all responsibilities of the workload team. + +## Next step + +:arrow_forward: [Deploy long-lifecycle resources](../03-supporting-services/README.md) diff --git a/avm/ptn/aca-lza/hosting-environment/modules/spoke/deploy.spoke.bicep b/avm/ptn/aca-lza/hosting-environment/modules/spoke/deploy.spoke.bicep new file mode 100644 index 0000000000..32587817dc --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/spoke/deploy.spoke.bicep @@ -0,0 +1,598 @@ +targetScope = 'subscription' + +// ------------------ +// PARAMETERS +// ------------------ + +// @description('The ID of the subscription to deploy the spoke resources to.') +// param subscriptionId string + +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('The location where the resources will be created. This should be the same region as the hub.') +param location string = deployment().location + +@description('Optional. The name of the resource group to create the resources in. If set, it overrides the name generated by the template.') +param spokeResourceGroupName string + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +// Hub +@description('The resource ID of the existing hub virtual network.') +param hubVNetId string + +@description('The resource id of the bastion host.') +param bastionResourceId string + +// Spoke +@description('CIDR of the spoke virtual network. For most landing zone implementations, the spoke network would have been created by your platform team.') +param spokeVNetAddressPrefixes array + +@description('Optional. The name of the subnet to create for the spoke infrastructure. If set, it overrides the name generated by the template.') +param spokeInfraSubnetName string = 'snet-infra' + +@description('CIDR of the spoke infrastructure subnet.') +param spokeInfraSubnetAddressPrefix string + +@description('Optional. The name of the subnet to create for the spoke private endpoints. If set, it overrides the name generated by the template.') +param spokePrivateEndpointsSubnetName string = 'snet-pep' + +@description('CIDR of the spoke private endpoints subnet.') +param spokePrivateEndpointsSubnetAddressPrefix string + +@description('Optional. The name of the subnet to create for the spoke application gateway. If set, it overrides the name generated by the template.') +param spokeApplicationGatewaySubnetName string = 'snet-agw' + +@description('CIDR of the spoke Application Gateway subnet. If the value is empty, this subnet will not be created.') +param spokeApplicationGatewaySubnetAddressPrefix string + +@description('The IP address of the network appliance (e.g. firewall) that will be used to route traffic to the internet.') +param networkApplianceIpAddress string + +@description('The size of the jump box virtual machine to create. See https://learn.microsoft.com/azure/virtual-machines/sizes for more information.') +param vmSize string + +@description('Optional. The zone to create the jump box in. Defaults to 0.') +param vmZone int = 0 + +@description('Optional. The storage account type to use for the jump box. Defaults to Standard_LRS.') +param storageAccountType string = 'Standard_LRS' + +@description('The username to use for the jump box.') +param vmAdminUsername string + +@description('The password to use for the jump box.') +@secure() +param vmAdminPassword string + +@description('The SSH public key to use for the jump box. Only relevant for Linux.') +@secure() +param vmLinuxSshAuthorizedKey string + +@description('The OS of the jump box virtual machine to create. If set to "none", no jump box will be created.') +@allowed(['linux', 'windows', 'none']) +param vmJumpboxOSType string = 'none' + +@description('Optional. The name of the subnet to create for the jump box. If set, it overrides the name generated by the template.') +param vmSubnetName string = 'snet-jumpbox' + +@description('CIDR to use for the jump box subnet.') +param vmJumpBoxSubnetAddressPrefix string + +@description('Type of authentication to use on the Virtual Machine. SSH key is recommended.') +@allowed([ + 'sshPublicKey' + 'password' +]) +param vmAuthenticationType string = 'password' + +// ------------------ +// VARIABLES +// ------------------ + +//Destination Service Tag for AzureCloud for Central France is centralfrance, but location is francecentral +var locationVar = location == 'francecentral' ? 'centralfrance' : location +var namingRules = json(loadTextContent('../naming/naming-rules.jsonc')) + +var rgSpokeName = !empty(spokeResourceGroupName) + ? spokeResourceGroupName + : '${namingRules.resourceTypeAbbreviations.resourceGroup}-${workloadName}-${environment}-${namingRules.regionAbbreviations[toLower(location)]}-spoke' + +// Subnet definition taking in consideration feature flags +var defaultSubnets = [ + { + name: spokeInfraSubnetName + addressPrefix: spokeInfraSubnetAddressPrefix + networkSecurityGroupResourceId: nsgContainerAppsEnvironment.outputs.resourceId + routeTableResourceId: (!empty(hubVNetId) && !empty(networkApplianceIpAddress)) + ? egressLockdownUdr.outputs.resourceId + : null + delegations: [ + { + name: 'envdelegation' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + { + name: spokePrivateEndpointsSubnetName + addressPrefix: spokePrivateEndpointsSubnetAddressPrefix + networkSecurityGroupResourceId: nsgPep.outputs.resourceId + } +] + +// Append optional application gateway subnet, if required +var appGwAndDefaultSubnets = !empty(spokeApplicationGatewaySubnetAddressPrefix) + ? concat(defaultSubnets, [ + { + name: spokeApplicationGatewaySubnetName + addressPrefix: spokeApplicationGatewaySubnetAddressPrefix + networkSecurityGroupResourceId: nsgAppGw.outputs.resourceId + } + ]) + : defaultSubnets + +//Append optional jumpbox subnet, if required +var spokeSubnets = vmJumpboxOSType != 'none' + ? concat(appGwAndDefaultSubnets, [ + { + name: vmSubnetName + addressPrefix: vmJumpBoxSubnetAddressPrefix + } + ]) + : appGwAndDefaultSubnets + +// ------------------ +// RESOURCES +// ------------------ + +module spokeResourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' = { + name: take('rg-${deployment().name}', 64) + params: { + name: rgSpokeName + location: location + enableTelemetry: enableTelemetry + tags: tags + } +} + +@description('User-configured naming rules') +module naming '../naming/naming.module.bicep' = { + scope: resourceGroup(rgSpokeName) + name: take('spokeNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(spokeResourceGroup.outputs.resourceId) + environment: environment + workloadName: workloadName + location: location + } +} + +@description('The spoke virtual network in which the workload will run from. This virtual network would normally already be provisioned by your subscription vending process, and only the subnets would need to be configured.') +module vnetSpoke 'br/public:avm/res/network/virtual-network:0.1.6' = { + name: take('vnetSpoke-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.vnetSpoke + location: location + tags: tags + enableTelemetry: enableTelemetry + addressPrefixes: spokeVNetAddressPrefixes + subnets: spokeSubnets + peerings: (!empty(hubVNetId)) + ? [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringEnabled: true + remotePeeringName: 'spokeToHub' + remoteVirtualNetworkId: hubVNetId + useRemoteGateways: false + } + ] + : null + } +} + +@description('The log sink for Azure Diagnostics') +module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.3.4' = { + name: take('logAnalyticsWs-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.logAnalyticsWorkspace + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + +@description('Network security group rules for the Container Apps cluster.') +module nsgContainerAppsEnvironment 'br/public:avm/res/network/network-security-group:0.2.0' = { + name: take('nsgContainerAppsEnvironment-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.containerAppsEnvironmentNsg + location: location + tags: tags + enableTelemetry: enableTelemetry + securityRules: [ + { + name: 'Allow_Internal_AKS_Connection_Between_Nodes_And_Control_Plane_UDP' + properties: { + description: 'internal AKS secure connection between underlying nodes and control plane..' + protocol: 'Udp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: 'AzureCloud.${locationVar}' + destinationPortRange: '1194' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + { + name: 'Allow_Internal_AKS_Connection_Between_Nodes_And_Control_Plane_TCP' + properties: { + description: 'internal AKS secure connection between underlying nodes and control plane..' + protocol: 'Tcp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: 'AzureCloud.${locationVar}' + destinationPortRange: '9000' + access: 'Allow' + priority: 110 + direction: 'Outbound' + } + } + { + name: 'Allow_Azure_Monitor' + properties: { + description: 'Allows outbound calls to Azure Monitor.' + protocol: 'Tcp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: 'AzureCloud.${locationVar}' + destinationPortRange: '443' + access: 'Allow' + priority: 120 + direction: 'Outbound' + } + } + { + name: 'Allow_Outbound_443' + properties: { + description: 'Allowing all outbound on port 443 provides a way to allow all FQDN based outbound dependencies that don\'t have a static IP' + protocol: 'Tcp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + access: 'Allow' + priority: 130 + direction: 'Outbound' + } + } + { + name: 'Allow_NTP_Server' + properties: { + description: 'NTP server' + protocol: 'Udp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '123' + access: 'Allow' + priority: 140 + direction: 'Outbound' + } + } + { + name: 'Allow_Container_Apps_control_plane' + properties: { + description: 'Container Apps control plane' + protocol: 'Tcp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRanges: [ + '5671' + '5672' + ] + access: 'Allow' + priority: 150 + direction: 'Outbound' + } + } + { + name: 'deny-hop-outbound' + properties: { + protocol: '*' + sourcePortRange: '*' + destinationPortRanges: [ + '3389' + '22' + ] + access: 'Deny' + priority: 200 + direction: 'Outbound' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + } + ] + diagnosticSettings: [ + { + name: 'logAnalyticsSettings' + workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + } + ] + } +} + +@description('NSG Rules for the Application Gateway.') +module nsgAppGw 'br/public:avm/res/network/network-security-group:0.2.0' = if (!empty(spokeApplicationGatewaySubnetAddressPrefix)) { + name: take('nsgAppGw-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.applicationGatewayNsg + location: location + tags: tags + enableTelemetry: enableTelemetry + securityRules: [ + { + name: 'HealthProbes' + properties: { + description: 'allow HealthProbes from gateway Manager.' + protocol: '*' + sourceAddressPrefix: 'GatewayManager' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '65200-65535' + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'Allow_TLS' + properties: { + description: 'allow https incoming connections' + protocol: '*' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + access: 'Allow' + priority: 110 + direction: 'Inbound' + } + } + { + name: 'Allow_HTTP' + properties: { + description: 'allow http incoming connections' + protocol: '*' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '80' + access: 'Allow' + priority: 120 + direction: 'Inbound' + } + } + { + name: 'Allow_AzureLoadBalancer' + properties: { + description: 'allow AzureLoadBalancer incoming connections' + protocol: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '80' + access: 'Allow' + priority: 130 + direction: 'Inbound' + } + } + { + name: 'allow-all-outbound' + properties: { + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + access: 'Allow' + priority: 210 + direction: 'Outbound' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + } + ] + diagnosticSettings: [ + { + name: 'logAnalyticsSettings' + workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + } + ] + } +} + +@description('NSG Rules for the private enpoint subnet.') +module nsgPep 'br/public:avm/res/network/network-security-group:0.2.0' = { + name: take('nsgPep-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.pepNsg + location: location + tags: tags + enableTelemetry: enableTelemetry + securityRules: [ + { + name: 'deny-hop-outbound' + properties: { + protocol: '*' + sourcePortRange: '*' + destinationPortRanges: [ + '3389' + '22' + ] + access: 'Deny' + priority: 200 + direction: 'Outbound' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + } + ] + diagnosticSettings: [ + { + name: 'logAnalyticsSettings' + workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + } + ] + } +} + +@description('The Route Table deployment') +module egressLockdownUdr 'br/public:avm/res/network/route-table:0.2.2' = if (!empty(hubVNetId) && !empty(networkApplianceIpAddress)) { + name: take('egressLockdownUdr-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + name: naming.outputs.resourcesNames.routeTable + location: location + tags: tags + enableTelemetry: enableTelemetry + routes: [ + { + name: 'defaultEgressLockdown' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'VirtualAppliance' + nextHopIpAddress: networkApplianceIpAddress + } + } + ] + } +} + +@description('An optional Linux virtual machine deployment to act as a jump box.') +module jumpboxLinuxVM '../compute/linux-vm.bicep' = if (vmJumpboxOSType == 'linux') { + name: take('vm-linux-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + vmName: naming.outputs.resourcesNames.vmJumpBox + bastionResourceId: bastionResourceId + vmAdminUsername: vmAdminUsername + vmAdminPassword: vmAdminPassword + vmSshPublicKey: vmLinuxSshAuthorizedKey + vmSize: vmSize + vmZone: vmZone + storageAccountType: storageAccountType + vmVnetName: vnetSpoke.outputs.name + vmSubnetName: vmSubnetName + vmSubnetAddressPrefix: vmJumpBoxSubnetAddressPrefix + vmNetworkInterfaceName: naming.outputs.resourcesNames.vmJumpBoxNic + vmNetworkSecurityGroupName: naming.outputs.resourcesNames.vmJumpBoxNsg + vmAuthenticationType: vmAuthenticationType + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + } +} + +@description('An optional Windows virtual machine deployment to act as a jump box.') +module jumpboxWindowsVM '../compute/windows-vm.bicep' = if (vmJumpboxOSType == 'windows') { + name: take('vm-windows-${deployment().name}', 64) + scope: resourceGroup(rgSpokeName) + params: { + location: location + tags: tags + enableTelemetry: enableTelemetry + vmName: naming.outputs.resourcesNames.vmJumpBox + bastionResourceId: bastionResourceId + vmAdminUsername: vmAdminUsername + vmAdminPassword: vmAdminPassword + vmSize: vmSize + vmZone: vmZone + storageAccountType: storageAccountType + vmVnetName: vnetSpoke.outputs.name + vmSubnetName: vmSubnetName + vmSubnetAddressPrefix: vmJumpBoxSubnetAddressPrefix + vmNetworkInterfaceName: naming.outputs.resourcesNames.vmJumpBoxNic + vmNetworkSecurityGroupName: naming.outputs.resourcesNames.vmJumpBoxNsg + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +resource vnetSpokeCreated 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { + name: vnetSpoke.outputs.name + scope: resourceGroup(rgSpokeName) + + resource spokeInfraSubnet 'subnets' existing = { + name: spokeInfraSubnetName + } + + resource spokePrivateEndpointsSubnet 'subnets' existing = { + name: spokePrivateEndpointsSubnetName + } + + resource spokeApplicationGatewaySubnet 'subnets' existing = if (!empty(spokeApplicationGatewaySubnetAddressPrefix)) { + name: spokeApplicationGatewaySubnetName + } +} + +@description('The name of the spoke resource group.') +output spokeResourceGroupName string = spokeResourceGroup.name + +@description('The resource ID of the spoke virtual network.') +output spokeVNetId string = vnetSpokeCreated.id + +@description('The name of the spoke virtual network.') +output spokeVNetName string = vnetSpokeCreated.name + +@description('The resource ID of the spoke infrastructure subnet.') +output spokeInfraSubnetId string = vnetSpokeCreated::spokeInfraSubnet.id + +@description('The name of the spoke infrastructure subnet.') +output spokeInfraSubnetName string = vnetSpokeCreated::spokeInfraSubnet.name + +@description('The name of the spoke private endpoints subnet.') +output spokePrivateEndpointsSubnetName string = vnetSpokeCreated::spokePrivateEndpointsSubnet.name + +@description('The resource ID of the spoke Application Gateway subnet. This is \'\' if the subnet was not created.') +output spokeApplicationGatewaySubnetId string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) + ? vnetSpokeCreated::spokeApplicationGatewaySubnet.id + : '' + +@description('The name of the spoke Application Gateway subnet. This is \'\' if the subnet was not created.') +output spokeApplicationGatewaySubnetName string = (!empty(spokeApplicationGatewaySubnetAddressPrefix)) + ? vnetSpokeCreated::spokeApplicationGatewaySubnet.name + : '' + +@description('The resource ID of the Azure Log Analytics Workspace.') +output logAnalyticsWorkspaceId string = logAnalyticsWorkspace.outputs.resourceId + +@description('The name of the jump box virtual machine.') +output vmJumpBoxName string = naming.outputs.resourcesNames.vmJumpBox diff --git a/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/README.md b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/README.md new file mode 100644 index 0000000000..730490c2b8 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/README.md @@ -0,0 +1,51 @@ +# Deploy the long-lifecycle resources + +At this point, you have a spoke virtual network ready to land your workload into. However, workloads have resources that live on different lifecycle cadences. Here you'll be deploying the resources that have a lifecycle longer than any of your application platform components. + +## Expected results + +Workloads often have resources that exist on different lifecycles. Some are singletons, and not tied to the deployment stamp of the application platform. Others come and go with the application platform and are part of the application's stamp. Yet others might even be tied to the deployment of code within the application platform. In this deployment, you'll be deploying resources that are not expected to be tied to the same lifecycle as the instance of the Azure Container App, and are in fact dependencies for any given instance and could be used by multiple instances if you had multiple stamps. + + +### Resources + +- Azure Container Registry +- Azure Key Vault +- Private Link for each, including related DNS Private Zone configuration +- User managed identities for the workload + +By default, they are deployed to the spoke resource group. + +## Steps + +1. Create the regional resources that the Azure Container Apps platform and its applications will be dependant on. + + ```bash + RESOURCEID_VNET_HUB=$(az deployment sub show -n acalza01-hub --query properties.outputs.hubVNetId.value -o tsv) + RESOURCENAME_RESOURCEGROUP_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeResourceGroupName.value -o tsv) + RESOURCEID_VNET_SPOKE=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.spokeVNetId.value -o tsv) + LOG_ANALYTICS_WS_ID=$(az deployment sub show -n acalza01-spokenetwork --query properties.outputs.logAnalyticsWorkspaceId.value -o tsv) + + echo RESOURCEID_VNET_HUB: $RESOURCEID_VNET_HUB && \ + echo RESOURCENAME_RESOURCEGROUP_SPOKE: $RESOURCENAME_RESOURCEGROUP_SPOKE && \ + echo RESOURCEID_VNET_SPOKE: $RESOURCEID_VNET_SPOKE && \ + echo LOG_ANALYTICS_WS_ID: $LOG_ANALYTICS_WS_ID + + # [This takes about four minutes to run (if you add deployRedis=false).] + az deployment group create \ + -n acalza01-dependencies \ + -g $RESOURCENAME_RESOURCEGROUP_SPOKE \ + -f 03-supporting-services/deploy.supporting-services.bicep \ + -p 03-supporting-services/deploy.supporting-services.parameters.jsonc \ + -p hubVNetId=${RESOURCEID_VNET_HUB} spokeVNetId=${RESOURCEID_VNET_SPOKE} \ + -p logAnalyticsWorkspaceId=${LOG_ANALYTICS_WS_ID} + ``` + +## Private DNS Zones + +Private DNS zones in this reference implementation are deployed at the hub level. For any resource that requires a private endpoint, the workload team (as part of the deployment of the LZA), will create the appropriate private DNS Zones & records, as well as the link to the Virtual Networks that need to resolve DNS names of the private Azure resources. The workload, in the LZA implementation, is using Azure DNS for resolution. Since this reference implementation is expected to be deployed isolated from existing infrastructure, Private DNS Zones are deployed in this way (in the hub, without Azure Policies etc), in order to resemple the recommendations of the CAF/ESLZ [Private Link and DNS integration at scale](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale), without over-complicating the Azure Container Apps LZA implementation. + + +## Next step + +:arrow_forward: [Deploy Azure Container Apps environment](../04-container-apps-environment/README.md) diff --git a/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/deploy.supporting-services.bicep b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/deploy.supporting-services.bicep new file mode 100644 index 0000000000..a9518d2e06 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/deploy.supporting-services.bicep @@ -0,0 +1,111 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string + +@description('The location where the resources will be created. This needs to be the same region as the spoke.') +param location string = resourceGroup().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deployment telemetry.') +param enableTelemetry bool + +// Hub +@description('The resource ID of the existing hub virtual network.') +param hubVNetId string + +// Spoke +@description('The resource ID of the existing spoke virtual network to which the private endpoint will be connected.') +param spokeVNetId string + +@description('The name of the existing subnet in the spoke virtual to which the private endpoint will be connected.') +param spokePrivateEndpointSubnetName string + +@description('Optional. Resource ID of the diagnostic log analytics workspace. If left empty, no diagnostics settings will be defined.') +param logAnalyticsWorkspaceId string = '' + +@description('Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false.') +param deployZoneRedundantResources bool = true + +// ------------------ +// RESOURCES +// ------------------ + +@description('User-configured naming rules') +module naming '../naming/naming.module.bicep' = { + name: take('supportingServicesNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(resourceGroup().id) + environment: environment + workloadName: workloadName + location: location + } +} + +@description('Azure Container Registry, where all workload images should be pulled from.') +module containerRegistry 'modules/container-registry.module.bicep' = { + name: 'containerRegistryModule-${uniqueString(resourceGroup().id)}' + params: { + containerRegistryName: naming.outputs.resourcesNames.containerRegistry + location: location + tags: tags + enableTelemetry: enableTelemetry + spokeVNetId: spokeVNetId + hubVNetId: hubVNetId + spokePrivateEndpointSubnetName: spokePrivateEndpointSubnetName + containerRegistryPrivateEndpointName: naming.outputs.resourcesNames.containerRegistryPep + containerRegistryUserAssignedIdentityName: naming.outputs.resourcesNames.containerRegistryUserAssignedIdentity + diagnosticWorkspaceId: logAnalyticsWorkspaceId + deployZoneRedundantResources: deployZoneRedundantResources + } +} + +@description('Azure Key Vault used to hold items like TLS certs and application secrets that your workload will need.') +module keyVault 'modules/key-vault.bicep' = { + name: 'keyVault-${uniqueString(resourceGroup().id)}' + params: { + keyVaultName: naming.outputs.resourcesNames.keyVault + location: location + tags: tags + enableTelemetry: enableTelemetry + spokeVNetId: spokeVNetId + hubVNetId: hubVNetId + spokePrivateEndpointSubnetName: spokePrivateEndpointSubnetName + keyVaultPrivateEndpointName: naming.outputs.resourcesNames.keyVaultPep + diagnosticWorkspaceId: logAnalyticsWorkspaceId + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of the Azure Container Registry.') +output containerRegistryId string = containerRegistry.outputs.containerRegistryId + +@description('The name of the Azure Container Registry.') +output containerRegistryName string = containerRegistry.outputs.containerRegistryName + +@description('The name of the container registry login server.') +output containerRegistryLoginServer string = containerRegistry.outputs.containerRegistryLoginServer + +@description('The resource ID of the user-assigned managed identity for the Azure Container Registry to be able to pull images from it.') +output containerRegistryUserAssignedIdentityId string = containerRegistry.outputs.containerRegistryUserAssignedIdentityId + +@description('The resource ID of the Azure Key Vault.') +output keyVaultResourceId string = keyVault.outputs.keyVaultId + +@description('The name of the Azure Key Vault.') +output keyVaultName string = keyVault.outputs.keyVaultName diff --git a/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/container-registry.module.bicep b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/container-registry.module.bicep new file mode 100644 index 0000000000..3168b3591b --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/container-registry.module.bicep @@ -0,0 +1,165 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The location where the resources will be created.') +param location string = resourceGroup().location + +@description('The name of the container registry.') +param containerRegistryName string + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +@description('Optional. The resource ID of the Hub Virtual Network.') +param hubVNetId string = '' + +@description('The resource ID of the VNet to which the private endpoint will be connected.') +param spokeVNetId string + +@description('The name of the subnet in the VNet to which the private endpoint will be connected.') +param spokePrivateEndpointSubnetName string + +@description('Optional. The name of the private endpoint to be created for Azure Container Registry. If left empty, it defaults to "-pep') +param containerRegistryPrivateEndpointName string = 'acr-pep' + +@description('The name of the user assigned identity to be created to pull image from Azure Container Registry.') +param containerRegistryUserAssignedIdentityName string + +@description('Required. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId string + +@description('Optional, default value is true. If true, any resources that support AZ will be deployed in all three AZ. However if the selected region is not supporting AZ, this parameter needs to be set to false.') +param deployZoneRedundantResources bool = true + +// ------------------ +// VARIABLES +// ------------------ + +var acrDnsZoneName = 'privatelink.azurecr.io' +var spokeVNetIdTokens = split(spokeVNetId, '/') +var spokeSubscriptionId = spokeVNetIdTokens[2] +var spokeResourceGroupName = spokeVNetIdTokens[4] +var spokeVNetName = spokeVNetIdTokens[8] +var containerRegistryPullRoleGuid = '7f951dda-4ed3-4680-a7ca-43fe172d538d' +var virtualNetworkLinks = concat( + [ + { + virtualNetworkResourceId: spokeVNetId + registrationEnabled: false + } + ], + (!empty(hubVNetId)) + ? [ + { + virtualNetworkResourceId: hubVNetId + registrationEnabled: false + } + ] + : [] +) +// ------------------ +// RESOURCES +// ------------------ +resource vnetSpoke 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + scope: resourceGroup(spokeSubscriptionId, spokeResourceGroupName) + name: spokeVNetName +} + +resource spokePrivateEndpointSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = { + parent: vnetSpoke + name: spokePrivateEndpointSubnetName +} + +module acrUserAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = { + name: containerRegistryUserAssignedIdentityName + params: { + name: containerRegistryUserAssignedIdentityName + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + +module acrdnszone 'br/public:avm/res/network/private-dns-zone:0.3.0' = { + name: 'acrDnsZoneDeployment-${uniqueString(resourceGroup().id)}' + params: { + name: acrDnsZoneName + location: 'global' + tags: tags + enableTelemetry: enableTelemetry + virtualNetworkLinks: virtualNetworkLinks + } +} + +module acr 'br/public:avm/res/container-registry/registry:0.3.0' = { + name: 'containerRegistry-${uniqueString(resourceGroup().id)}' + params: { + name: containerRegistryName + location: location + tags: tags + enableTelemetry: enableTelemetry + acrSku: 'Premium' + publicNetworkAccess: 'Disabled' + acrAdminUserEnabled: false + networkRuleBypassOptions: 'AzureServices' + zoneRedundancy: deployZoneRedundantResources ? 'Enabled' : 'Disabled' + trustPolicyStatus: 'enabled' + diagnosticSettings: [ + { + name: 'acr-log-analytics' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + workspaceResourceId: diagnosticWorkspaceId + } + ] + privateEndpoints: [ + { + name: containerRegistryPrivateEndpointName + privateDnsZoneResourceIds: [ + acrdnszone.outputs.resourceId + ] + subnetResourceId: spokePrivateEndpointSubnet.id + } + ] + quarantinePolicyStatus: 'enabled' + roleAssignments: [ + { + principalId: acrUserAssignedIdentity.outputs.principalId + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: containerRegistryPullRoleGuid + } + ] + softDeletePolicyDays: 7 + softDeletePolicyStatus: 'disabled' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of the container registry.') +output containerRegistryId string = acr.outputs.resourceId + +@description('The name of the container registry.') +output containerRegistryName string = acr.outputs.name + +@description('The name of the container registry login server.') +output containerRegistryLoginServer string = acr.outputs.loginServer + +@description('The resource ID of the user assigned managed identity for the container registry to be able to pull images from it.') +output containerRegistryUserAssignedIdentityId string = acrUserAssignedIdentity.outputs.resourceId diff --git a/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/key-vault.bicep b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/key-vault.bicep new file mode 100644 index 0000000000..97a03c2c6f --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/modules/supporting-services/modules/key-vault.bicep @@ -0,0 +1,139 @@ +targetScope = 'resourceGroup' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The location where the resources will be created.') +param location string = resourceGroup().location + +@description('The name of the Key Vault.') +param keyVaultName string + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +@description('The resource ID of the Hub Virtual Network.') +param hubVNetId string + +@description('The resource ID of the VNet to which the private endpoint will be connected.') +param spokeVNetId string + +@description('The name of the subnet in the VNet to which the private endpoint will be connected.') +param spokePrivateEndpointSubnetName string + +@description('Optional. The name of the private endpoint to be created for Key Vault. If left empty, it defaults to "-pep') +param keyVaultPrivateEndpointName string = 'keyvault-pep' + +@description('Required. 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.') +param diagnosticWorkspaceId string + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = 'keyvault-diagnosticSettings' + +// ------------------ +// VARIABLES +// ------------------ + +var vaultDnsZoneName = 'privatelink.vaultcore.azure.net' +var spokeVNetIdTokens = split(spokeVNetId, '/') +var spokeSubscriptionId = spokeVNetIdTokens[2] +var spokeResourceGroupName = spokeVNetIdTokens[4] +var spokeVNetName = spokeVNetIdTokens[8] +var virtualNetworkLinks = concat( + [ + { + virtualNetworkResourceId: spokeVNetId + registrationEnabled: false + } + ], + (!empty(hubVNetId)) + ? [ + { + virtualNetworkResourceId: hubVNetId + registrationEnabled: false + } + ] + : [] +) +// ------------------ +// RESOURCES +// ------------------ + +resource vnetSpoke 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + scope: resourceGroup(spokeSubscriptionId, spokeResourceGroupName) + name: spokeVNetName +} + +resource spokePrivateEndpointSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' existing = { + parent: vnetSpoke + name: spokePrivateEndpointSubnetName +} + +module vaultdnszone 'br/public:avm/res/network/private-dns-zone:0.3.0' = { + name: 'keyvaultDnsZoneDeployment-${uniqueString(resourceGroup().id)}' + params: { + name: vaultDnsZoneName + location: 'global' + tags: tags + enableTelemetry: enableTelemetry + virtualNetworkLinks: virtualNetworkLinks + } +} + +module keyvault 'br/public:avm/res/key-vault/vault:0.6.1' = { + name: 'vault-${uniqueString(resourceGroup().id)}' + params: { + name: keyVaultName + location: location + tags: tags + enableTelemetry: enableTelemetry + sku: 'standard' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + enableSoftDelete: true + softDeleteRetentionInDays: 7 + enablePurgeProtection: null + publicNetworkAccess: 'Disabled' + enableRbacAuthorization: true + enableVaultForDeployment: true + privateEndpoints: [ + { + name: keyVaultPrivateEndpointName + privateDnsZoneResourceIds: [ + vaultdnszone.outputs.resourceId + ] + subnetResourceId: spokePrivateEndpointSubnet.id + } + ] + diagnosticSettings: [ + { + name: diagnosticSettingsName + workspaceResourceId: diagnosticWorkspaceId + logCategoriesAndGroups: [ + { categoryGroup: 'allLogs' } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + } + ] + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of the key vault.') +output keyVaultId string = keyvault.outputs.resourceId + +@description('The name of the key vault.') +output keyVaultName string = keyvault.outputs.name diff --git a/avm/ptn/aca-lza/hosting-environment/tests/e2e/defaults/main.test.bicep b/avm/ptn/aca-lza/hosting-environment/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..e23cf90aa2 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,54 @@ +metadata name = 'Using only defaults.' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'acalzamin' + +@description('Optional. Test name prefix.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + workloadName: serviceShort + tags: { + environment: 'test' + } + location: resourceLocation + vmSize: 'Standard_B1s' + vmAdminUsername: 'vmadmin' + vmAdminPassword: password + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + enableApplicationInsights: true + enableDaprInstrumentation: false + applicationGatewayCertificateKeyName: 'appgwcert' + } +} + +output testDeploymentOutputs object = testDeployment.outputs diff --git a/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/deploy.hub.bicep b/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/deploy.hub.bicep new file mode 100644 index 0000000000..d10701bf29 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/deploy.hub.bicep @@ -0,0 +1,476 @@ +targetScope = 'subscription' + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The name of the workload that is being deployed. Up to 10 characters long.') +@minLength(2) +@maxLength(10) +param workloadName string + +@description('The name of the environment (e.g. "dev", "test", "prod", "uat", "dr", "qa"). Up to 8 characters long.') +@maxLength(8) +param environment string = 'test' + +@description('The location where the resources will be created.') +param location string = deployment().location + +@description('Optional. The tags to be assigned to the created resources.') +param tags object = {} + +@description('Required. Whether to enable deplotment telemetry.') +param enableTelemetry bool + +@description('CIDR of the hub virtual network.') +param vnetAddressPrefixes array + +@description('Enable or disable the creation of the Azure Bastion.') +param enableBastion bool = true + +@description('Bastion sku, default is basic') +@allowed([ + 'Basic' + 'Standard' +]) +param bastionSku string = 'Basic' + +@description('CIDR to use for the Azure Bastion subnet.') +param bastionSubnetAddressPrefix string + +@description('CIDR to use for the gatewaySubnet.') +param gatewaySubnetAddressPrefix string + +@description('CIDR to use for the azureFirewallSubnet.') +param azureFirewallSubnetAddressPrefix string + +//@description('CIDR to use for the AzureFirewallManagementSubnet, which is required by AzFW Basic.') +//param azureFirewallSubnetManagementAddressPrefix string + +// ------------------ +// VARIABLES +// ------------------ + +// These cannot be another value +var gatewaySubnetName = 'GatewaySubnet' +var azureFirewallSubnetName = 'AzureFirewallSubnet' +//var AzureFirewallManagementSubnetName = 'AzureFirewallManagementSubnet' +var bastionSubnetName = 'AzureBastionSubnet' + +//Subnet definition taking in consideration feature flags +var defaultSubnets = [ + { + name: gatewaySubnetName + addressPrefix: gatewaySubnetAddressPrefix + } + { + name: azureFirewallSubnetName + addressPrefix: azureFirewallSubnetAddressPrefix + } + // { + // name: AzureFirewallManagementSubnetName + // addressPrefix: azureFirewallSubnetManagementAddressPrefix + // } +] + +// Append optional bastion subnet, if required +var vnetSubnets = enableBastion + ? concat(defaultSubnets, [ + { + name: bastionSubnetName + addressPrefix: bastionSubnetAddressPrefix + } + ]) + : defaultSubnets + +//used only to override the RG name - because it is created at the subscription level, the naming module cannot be loaded/used +var namingRules = json(loadTextContent('../../../modules/naming/naming-rules.jsonc')) +var rgHubName = '${namingRules.resourceTypeAbbreviations.resourceGroup}-${workloadName}-hub-${environment}-${namingRules.regionAbbreviations[toLower(location)]}' +var spokeInfraSubnetAddressPrefix = '10.1.0.0/23' +var applicationRuleCollections = [ + { + name: 'ace-allow-rules' + properties: { + action: { + type: 'Allow' + } + priority: 110 + rules: [ + { + name: 'ace-general-allow-rules' + protocols: [ + { + port: 80 + protocolType: 'Http' + } + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + targetFqdns: [ + 'mcr.microsoft.com' + '*.data.mcr.microsoft.com' + '*.blob.${az.environment().suffixes.storage}' //NOTE: If you use ACR the endpoint must be added as well. + ] + } + { + name: 'ace-acr-and-docker-allow-rules' + protocols: [ + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + targetFqdns: [ + '*.blob.${az.environment().suffixes.storage}' + 'login.microsoft.com' + '*.azurecr.io' //NOTE: for less permisive environment replace wildcard with actual(s) Container Registries + 'hub.docker.com' + 'registry-1.docker.io' + 'production.cloudflare.docker.com' + ] + } + { + name: 'ace-managed-identity-allow-rules' + protocols: [ + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + targetFqdns: [ + '*.identity.azure.net' + 'login.microsoftonline.com' + '*.login.microsoftonline.com' + '*.login.microsoft.com' + ] + } + { + name: 'ace-keyvault-allow-rules' + protocols: [ + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + targetFqdns: [ + '*${az.environment().suffixes.keyvaultDns}' //NOTE: for less permisive environment replace wildcard with actual(s) KeyVault + #disable-next-line no-hardcoded-env-urls + 'login.microsoft.com' + ] + } + ] + } + } + { + name: 'allow_azure_monitor' + properties: { + action: { + type: 'Allow' + } + priority: 120 + rules: [ + { + fqdnTags: [] + targetFqdns: [ + 'dc.applicationinsights.azure.com' + 'dc.applicationinsights.microsoft.com' + 'dc.services.visualstudio.com' + '*.in.applicationinsights.azure.com' + 'live.applicationinsights.azure.com' + 'rt.applicationinsights.microsoft.com' + 'rt.services.visualstudio.com' + '*.livediagnostics.monitor.azure.com' + '*.monitoring.azure.com' + 'agent.azureserviceprofiler.net' + '*.agent.azureserviceprofiler.net' + '*.monitor.azure.com' + ] + name: 'allow-azure-monitor' + protocols: [ + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + } + ] + } + } + { + name: 'allow_core_dev_fqdn' //NOTE: This rule is optional, and used here only to demonstrate that there are possibly more fqdns that need to be allowed, depending on your scenario + properties: { + action: { + type: 'Allow' + } + priority: 130 + rules: [ + { + name: 'allow-developer-services' + fqdnTags: [] + targetFqdns: [ + 'github.com' + '*.github.com' + 'ghcr.io' + '*.ghcr.io' + '*.nuget.org' + '*.blob.${az.environment().suffixes.storage}' // might replace wildcard with specific FQDN + '*.table.${az.environment().suffixes.storage}' // might replace wildcard with specific FQDN + '*.servicebus.windows.net' // might replace wildcard with specific FQDN + 'githubusercontent.com' + '*.githubusercontent.com' + 'dev.azure.com' + 'portal.azure.com' + '*.portal.azure.com' + '*.portal.azure.net' + 'appservice.azureedge.net' + '*.azurewebsites.net' + ] + protocols: [ + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + } + { + name: 'allow-certificate-dependencies' + fqdnTags: [] + targetFqdns: [ + '*.delivery.mp.microsoft.com' + 'ctldl.windowsupdate.com' + 'ocsp.msocsp.com' + 'oneocsp.microsoft.com' + 'crl.microsoft.com' + 'www.microsoft.com' + '*.digicert.com' + '*.symantec.com' + '*.symcb.com' + '*.d-trust.net' + ] + protocols: [ + { + port: 80 + protocolType: 'Http' + } + { + port: 443 + protocolType: 'Https' + } + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + } + ] + } + } +] +var networkRules = [ + { + name: 'ace-allow-rules' + properties: { + action: { + type: 'Allow' + } + priority: 100 + // For more Azure resources (than KeyVault, ACR etc which we use here) you are using with Azure Firewall, + // please refer to the service tags documentation: https://learn.microsoft.com/azure/virtual-network/service-tags-overview#available-service-tags + rules: [ + { + name: 'ace-general-allow-rule' + protocols: [ + 'Any' + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + destinationAddresses: [ + 'MicrosoftContainerRegistry' //For even less permisive environment, you can point to a specific MCR region, i.e. 'MicrosoftContainerRegistry.Westeurope' + 'AzureFrontDoor.FirstParty' + ] + destinationPorts: [ + '443' + ] + } + { + name: 'ace-acr-allow-rule' + protocols: [ + 'Any' + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + destinationAddresses: [ + 'AzureContainerRegistry' //For even less permisive environment, you can point to a specific ACR region, i.e. 'MicrosoftContainerRegistry.Westeurope' + 'AzureActiveDirectory' + ] + destinationPorts: [ + '443' + ] + } + { + name: 'ace-keyvault-allow-rule' + protocols: [ + 'Any' + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + destinationAddresses: [ + 'AzureKeyVault' //For even less permisive environment, you can point to a specific keyvault region, i.e. 'MicrosoftContainerRegistry.Westeurope' + 'AzureActiveDirectory' + ] + destinationPorts: [ + '443' + ] + } + { + name: 'ace-managedIdentity-allow-rule' + protocols: [ + 'Any' + ] + sourceAddresses: [ + spokeInfraSubnetAddressPrefix + ] + destinationAddresses: [ + 'AzureActiveDirectory' + ] + destinationPorts: [ + '443' + ] + } + ] + } + } +] +// ------------------ +// RESOURCES +// ------------------ +@description('User-configured naming rules') +module naming '../../../modules/naming/naming.module.bicep' = { + scope: resourceGroup(rgHubName) + name: take('hubNamingDeployment-${deployment().name}', 64) + params: { + uniqueId: uniqueString(hubResourceGroup.outputs.resourceId) + environment: environment + workloadName: workloadName + location: location + } +} + +@description('The hub resource group. This would normally be already provisioned by your platform team.') +module hubResourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' = { + name: take('rg-${deployment().name}', 64) + params: { + name: rgHubName + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + +@description('The log sink for Azure Diagnostics') +module hubLogAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.3.4' = { + name: take('logAnalyticsWs-${uniqueString(rgHubName)}', 64) + scope: resourceGroup(rgHubName) + params: { + name: naming.outputs.resourcesNames.logAnalyticsWorkspace + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + +@description('The virtual network used as the stand-in for the regional hub. This would normally be already provisioned by your platform team.') +module vnetHub 'br/public:avm/res/network/virtual-network:0.1.6' = { + name: take('vnetHub-${deployment().name}', 64) + scope: resourceGroup(rgHubName) + params: { + name: naming.outputs.resourcesNames.vnetHub + location: location + tags: tags + enableTelemetry: enableTelemetry + addressPrefixes: vnetAddressPrefixes + subnets: vnetSubnets + } +} + +@description('The Azure Firewall deployment. This would normally be already provisioned by your platform team.') +module azureFirewall 'br/public:avm/res/network/azure-firewall:0.3.2' = { + name: take('afw-${deployment().name}', 64) + scope: resourceGroup(rgHubName) + params: { + name: naming.outputs.resourcesNames.azureFirewall + azureSkuTier: 'Standard' + location: location + tags: tags + enableTelemetry: enableTelemetry + virtualNetworkResourceId: vnetHub.outputs.resourceId + applicationRuleCollections: applicationRuleCollections + networkRuleCollections: networkRules + diagnosticSettings: [ + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'azfw-diag' + workspaceResourceId: hubLogAnalyticsWorkspace.outputs.resourceId + } + ] + } +} + +@description('An optional Azure Bastion deployment for jump box access. This would normally be already provisioned by your platform team.') +module bastionHost 'br/public:avm/res/network/bastion-host:0.2.1' = { + name: take('bastion-${deployment().name}', 64) + scope: resourceGroup(rgHubName) + params: { + // Required parameters + name: naming.outputs.resourcesNames.bastion + virtualNetworkResourceId: vnetHub.outputs.resourceId + // Non-required parameters + location: location + tags: tags + enableTelemetry: enableTelemetry + enableFileCopy: true + skuName: bastionSku + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The resource ID of hub virtual network.') +output hubVNetId string = vnetHub.outputs.resourceId + +@description('The name of hub virtual network') +output hubVnetName string = vnetHub.outputs.name + +@description('The name of the hub resource group.') +output resourceGroupName string = hubResourceGroup.outputs.name + +@description('The private IP address of the Azure Firewall.') +output networkApplianceIpAddress string = azureFirewall.outputs.privateIp diff --git a/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/main.test.bicep b/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/main.test.bicep new file mode 100644 index 0000000000..f0edb1d3d0 --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/tests/e2e/hub-spoke/main.test.bicep @@ -0,0 +1,84 @@ +metadata name = 'Using a hub and spoke deployment.' +metadata description = 'This instance deploys the module including a Hub to peer to.' + +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'hubspoke' + +@description('Optional. Test name prefix.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ================= // +// Variables Section // +// ================= // + +// ============ // +// Dependencies // +// ============ // +module hubdeployment 'deploy.hub.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-hub-${serviceShort}' + params: { + location: resourceLocation + tags: { + environment: 'test' + } + enableTelemetry: true + vnetAddressPrefixes: ['10.0.0.0/24'] + azureFirewallSubnetAddressPrefix: '10.0.0.64/26' + //azureFirewallSubnetManagementAddressPrefix: '10.0.0.128/26' + gatewaySubnetAddressPrefix: '10.0.0.0/27' + bastionSubnetAddressPrefix: '10.0.0.192/26' + workloadName: serviceShort + } +} + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + workloadName: serviceShort + hubVirtualNetworkResourceId: hubdeployment.outputs.hubVNetId + networkApplianceIpAddress: hubdeployment.outputs.networkApplianceIpAddress + tags: { + environment: 'test' + } + environment: 'dev' + location: resourceLocation + vmSize: 'Standard_B1s' + storageAccountType: 'Premium_LRS' + vmAdminUsername: 'vmadmin' + vmAdminPassword: password + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + enableApplicationInsights: true + enableDaprInstrumentation: false + applicationGatewayCertificateKeyName: 'appgwcert' + deployZoneRedundantResources: true + exposeContainerAppsWith: 'applicationGateway' + enableDdosProtection: true + } +} + +output testDeploymentOutputs object = testDeployment.outputs diff --git a/avm/ptn/aca-lza/hosting-environment/tests/e2e/waf-aligned/main.test.bicep b/avm/ptn/aca-lza/hosting-environment/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..56da49fa5f --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,59 @@ +metadata name = 'Using all the available options in WAF aligned values.' +metadata description = 'This instance deploys the module with the all the available parameters in WAF aligned values.' + +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test +param serviceShort string = 'acawaf' + +@description('Optional. Test name prefix.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + workloadName: serviceShort + tags: { + environment: 'test' + } + environment: 'dev' + location: resourceLocation + vmSize: 'Standard_B1s' + storageAccountType: 'Premium_LRS' + vmAdminUsername: 'vmadmin' + vmAdminPassword: password + vmLinuxSshAuthorizedKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9QWdPia7CYYWWX/+eRrLKzGtQ+tjelZfDlbHy/Dg98 konstantinospantos@KonstaninossMBP.localdomain' + vmAuthenticationType: 'sshPublicKey' + vmJumpboxOSType: 'linux' + vmJumpBoxSubnetAddressPrefix: '10.1.2.32/27' + spokeVNetAddressPrefixes: [ + '10.1.0.0/22' + ] + spokeInfraSubnetAddressPrefix: '10.1.0.0/23' + spokePrivateEndpointsSubnetAddressPrefix: '10.1.2.0/27' + spokeApplicationGatewaySubnetAddressPrefix: '10.1.3.0/24' + enableApplicationInsights: true + enableDaprInstrumentation: false + applicationGatewayCertificateKeyName: 'appgwcert' + deployZoneRedundantResources: true + exposeContainerAppsWith: 'applicationGateway' + enableDdosProtection: true + } +} + +output testDeploymentOutputs object = testDeployment.outputs diff --git a/avm/ptn/aca-lza/hosting-environment/version.json b/avm/ptn/aca-lza/hosting-environment/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/aca-lza/hosting-environment/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/ptn/ai-platform/baseline/README.md b/avm/ptn/ai-platform/baseline/README.md new file mode 100644 index 0000000000..804d109fa6 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/README.md @@ -0,0 +1,1189 @@ +# AI Platform Baseline `[AiPlatform/Baseline]` + +This module provides a secure and scalable environment for deploying AI applications on Azure. +The module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services. +By integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Automanage/configurationProfileAssignments` | [2022-05-04](https://learn.microsoft.com/en-us/azure/templates) | +| `Microsoft.Compute/virtualMachines` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-09-01/virtualMachines) | +| `Microsoft.Compute/virtualMachines/extensions` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines/extensions) | +| `Microsoft.ContainerRegistry/registries` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries) | +| `Microsoft.ContainerRegistry/registries/cacheRules` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/cacheRules) | +| `Microsoft.ContainerRegistry/registries/replications` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/replications) | +| `Microsoft.ContainerRegistry/registries/webhooks` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/webhooks) | +| `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | +| `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | +| `Microsoft.Insights/components` | [2020-02-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-02-02/components) | +| `microsoft.insights/components/linkedStorageAccounts` | [2020-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2020-03-01-preview/components/linkedStorageAccounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults) | +| `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | +| `Microsoft.KeyVault/vaults/keys` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/keys) | +| `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | +| `Microsoft.MachineLearningServices/workspaces` | [2024-04-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2024-04-01-preview/workspaces) | +| `Microsoft.MachineLearningServices/workspaces/computes` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2022-10-01/workspaces/computes) | +| `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | +| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities) | +| `Microsoft.Network/bastionHosts` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/bastionHosts) | +| `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | +| `Microsoft.Network/networkSecurityGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkSecurityGroups) | +| `Microsoft.Network/privateDnsZones` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones) | +| `Microsoft.Network/privateDnsZones/A` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | +| `Microsoft.Network/privateDnsZones/AAAA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | +| `Microsoft.Network/privateDnsZones/CNAME` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | +| `Microsoft.Network/privateDnsZones/MX` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | +| `Microsoft.Network/privateDnsZones/PTR` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | +| `Microsoft.Network/privateDnsZones/SOA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | +| `Microsoft.Network/privateDnsZones/SRV` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | +| `Microsoft.Network/privateDnsZones/TXT` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | +| `Microsoft.OperationalInsights/workspaces` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces) | +| `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | +| `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | +| `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/ai-platform/baseline:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [Without virtual machine](#example-3-without-virtual-machine) +- [Without virtual network](#example-4-without-virtual-network) +- [WAF-aligned](#example-5-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { + name: 'baselineDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + virtualMachineConfiguration: { + adminPassword: '' + adminUsername: 'localAdminUser' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "virtualMachineConfiguration": { + "value": { + "adminPassword": "", + "adminUsername": "localAdminUser" + } + } + } +} +``` + +
+

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { + name: 'baselineDeployment' + params: { + // Required parameters + name: 'aipbmax' + // Non-required parameters + applicationInsightsConfiguration: { + name: 'appi-aipbmax' + } + bastionConfiguration: { + disableCopyPaste: true + enabled: true + enableFileCopy: true + enableIpConnect: true + enableKerberos: true + enableShareableLink: true + name: 'bas-aipbmax' + networkSecurityGroupResourceId: '' + scaleUnits: 3 + sku: 'Standard' + subnetAddressPrefix: '10.1.1.0/26' + } + containerRegistryConfiguration: { + name: 'craipbmax' + trustPolicyStatus: 'disabled' + } + keyVaultConfiguration: { + enablePurgeProtection: false + name: '' + } + logAnalyticsConfiguration: { + name: 'log-aipbmax' + } + managedIdentityConfiguration: { + hubName: 'id-hub-aipbmax' + projectName: 'id-project-aipbmax' + } + storageAccountConfiguration: { + allowSharedKeyAccess: true + name: 'staipbmax' + sku: 'Standard_GRS' + } + virtualMachineConfiguration: { + adminPassword: '' + adminUsername: 'localAdminUser' + enableAadLoginExtension: true + enableAzureMonitorAgent: true + enabled: true + encryptionAtHost: false + imageReference: { + offer: 'dsvm-win-2022' + publisher: 'microsoft-dsvm' + sku: 'winserver-2022' + version: 'latest' + } + maintenanceConfigurationResourceId: '' + name: '' + nicConfigurationConfiguration: { + ipConfigName: 'ipcfg-aipbmax' + name: 'nic-aipbmax' + networkSecurityGroupResourceId: '' + privateIPAllocationMethod: 'Dynamic' + } + osDisk: { + caching: 'ReadOnly' + createOption: 'FromImage' + deleteOption: 'Delete' + diskSizeGB: 256 + managedDisk: { + storageAccountType: 'Standard_LRS' + } + name: 'disk-aipbmax' + } + patchMode: 'AutomaticByPlatform' + size: 'Standard_DS1_v2' + zone: 0 + } + virtualNetworkConfiguration: { + addressPrefix: '10.1.0.0/16' + enabled: true + name: 'vnet-aipbmax' + subnet: { + addressPrefix: '10.1.0.0/24' + name: 'snet-aipbmax' + networkSecurityGroupResourceId: '' + } + } + workspaceConfiguration: { + computes: [ + { + computeType: 'ComputeInstance' + description: 'Default' + location: '' + name: '' + properties: { + vmSize: 'STANDARD_DS11_V2' + } + sku: 'Standard' + } + ] + name: 'hub-aipbmax' + networkIsolationMode: 'AllowOnlyApprovedOutbound' + networkOutboundRules: { + rule1: { + category: 'UserDefined' + destination: 'pypi.org' + type: 'FQDN' + } + } + projectName: 'project-aipbmax' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "aipbmax" + }, + // Non-required parameters + "applicationInsightsConfiguration": { + "value": { + "name": "appi-aipbmax" + } + }, + "bastionConfiguration": { + "value": { + "disableCopyPaste": true, + "enabled": true, + "enableFileCopy": true, + "enableIpConnect": true, + "enableKerberos": true, + "enableShareableLink": true, + "name": "bas-aipbmax", + "networkSecurityGroupResourceId": "", + "scaleUnits": 3, + "sku": "Standard", + "subnetAddressPrefix": "10.1.1.0/26" + } + }, + "containerRegistryConfiguration": { + "value": { + "name": "craipbmax", + "trustPolicyStatus": "disabled" + } + }, + "keyVaultConfiguration": { + "value": { + "enablePurgeProtection": false, + "name": "" + } + }, + "logAnalyticsConfiguration": { + "value": { + "name": "log-aipbmax" + } + }, + "managedIdentityConfiguration": { + "value": { + "hubName": "id-hub-aipbmax", + "projectName": "id-project-aipbmax" + } + }, + "storageAccountConfiguration": { + "value": { + "allowSharedKeyAccess": true, + "name": "staipbmax", + "sku": "Standard_GRS" + } + }, + "virtualMachineConfiguration": { + "value": { + "adminPassword": "", + "adminUsername": "localAdminUser", + "enableAadLoginExtension": true, + "enableAzureMonitorAgent": true, + "enabled": true, + "encryptionAtHost": false, + "imageReference": { + "offer": "dsvm-win-2022", + "publisher": "microsoft-dsvm", + "sku": "winserver-2022", + "version": "latest" + }, + "maintenanceConfigurationResourceId": "", + "name": "", + "nicConfigurationConfiguration": { + "ipConfigName": "ipcfg-aipbmax", + "name": "nic-aipbmax", + "networkSecurityGroupResourceId": "", + "privateIPAllocationMethod": "Dynamic" + }, + "osDisk": { + "caching": "ReadOnly", + "createOption": "FromImage", + "deleteOption": "Delete", + "diskSizeGB": 256, + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "name": "disk-aipbmax" + }, + "patchMode": "AutomaticByPlatform", + "size": "Standard_DS1_v2", + "zone": 0 + } + }, + "virtualNetworkConfiguration": { + "value": { + "addressPrefix": "10.1.0.0/16", + "enabled": true, + "name": "vnet-aipbmax", + "subnet": { + "addressPrefix": "10.1.0.0/24", + "name": "snet-aipbmax", + "networkSecurityGroupResourceId": "" + } + } + }, + "workspaceConfiguration": { + "value": { + "computes": [ + { + "computeType": "ComputeInstance", + "description": "Default", + "location": "", + "name": "", + "properties": { + "vmSize": "STANDARD_DS11_V2" + }, + "sku": "Standard" + } + ], + "name": "hub-aipbmax", + "networkIsolationMode": "AllowOnlyApprovedOutbound", + "networkOutboundRules": { + "rule1": { + "category": "UserDefined", + "destination": "pypi.org", + "type": "FQDN" + } + }, + "projectName": "project-aipbmax" + } + } + } +} +``` + +
+

+ +### Example 3: _Without virtual machine_ + +This instance deploys the module with a virtual network, but no virtual machine or Azure Bastion host. + + +

+ +via Bicep module + +```bicep +module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { + name: 'baselineDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + bastionConfiguration: { + enabled: false + } + virtualMachineConfiguration: { + enabled: false + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "bastionConfiguration": { + "value": { + "enabled": false + } + }, + "virtualMachineConfiguration": { + "value": { + "enabled": false + } + } + } +} +``` + +
+

+ +### Example 4: _Without virtual network_ + +This instance deploys the module without a virtual network, virtual machine or Azure Bastion host. + + +

+ +via Bicep module + +```bicep +module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { + name: 'baselineDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + virtualNetworkConfiguration: { + enabled: false + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "virtualNetworkConfiguration": { + "value": { + "enabled": false + } + } + } +} +``` + +
+

+ +### Example 5: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { + name: 'baselineDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + tags: { + Env: 'test' + 'hidden-title': 'This is visible in the resource name' + } + virtualMachineConfiguration: { + adminPassword: '' + adminUsername: 'localAdminUser' + enableAadLoginExtension: true + enableAzureMonitorAgent: true + maintenanceConfigurationResourceId: '' + patchMode: 'AutomaticByPlatform' + zone: 1 + } + workspaceConfiguration: { + networkIsolationMode: 'AllowOnlyApprovedOutbound' + networkOutboundRules: { + rule: { + category: 'UserDefined' + destination: { + serviceResourceId: '' + subresourceTarget: 'blob' + } + type: 'PrivateEndpoint' + } + } + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "tags": { + "value": { + "Env": "test", + "hidden-title": "This is visible in the resource name" + } + }, + "virtualMachineConfiguration": { + "value": { + "adminPassword": "", + "adminUsername": "localAdminUser", + "enableAadLoginExtension": true, + "enableAzureMonitorAgent": true, + "maintenanceConfigurationResourceId": "", + "patchMode": "AutomaticByPlatform", + "zone": 1 + } + }, + "workspaceConfiguration": { + "value": { + "networkIsolationMode": "AllowOnlyApprovedOutbound", + "networkOutboundRules": { + "rule": { + "category": "UserDefined", + "destination": { + "serviceResourceId": "", + "subresourceTarget": "blob" + }, + "type": "PrivateEndpoint" + } + } + } + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Alphanumberic suffix to use for resource naming. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationInsightsConfiguration`](#parameter-applicationinsightsconfiguration) | object | Configuration for Application Insights. | +| [`bastionConfiguration`](#parameter-bastionconfiguration) | object | Configuration for the Azure Bastion host. | +| [`containerRegistryConfiguration`](#parameter-containerregistryconfiguration) | object | Configuration for the container registry. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`keyVaultConfiguration`](#parameter-keyvaultconfiguration) | object | Configuration for the key vault. | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`logAnalyticsConfiguration`](#parameter-loganalyticsconfiguration) | object | Configuration for the Log Analytics workspace. | +| [`managedIdentityConfiguration`](#parameter-managedidentityconfiguration) | object | Configuration for the user-assigned managed identities. | +| [`storageAccountConfiguration`](#parameter-storageaccountconfiguration) | object | Configuration for the storage account. | +| [`tags`](#parameter-tags) | object | Resource tags. | +| [`virtualMachineConfiguration`](#parameter-virtualmachineconfiguration) | secureObject | Configuration for the virtual machine. | +| [`virtualNetworkConfiguration`](#parameter-virtualnetworkconfiguration) | object | Configuration for the virtual network. | +| [`workspaceConfiguration`](#parameter-workspaceconfiguration) | object | Configuration for the AI Studio workspace. | + +### Parameter: `name` + +Alphanumberic suffix to use for resource naming. + +- Required: Yes +- Type: string + +### Parameter: `applicationInsightsConfiguration` + +Configuration for Application Insights. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-applicationinsightsconfigurationname) | string | The name of the Application Insights resource. | + +### Parameter: `applicationInsightsConfiguration.name` + +The name of the Application Insights resource. + +- Required: No +- Type: string + +### Parameter: `bastionConfiguration` + +Configuration for the Azure Bastion host. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`disableCopyPaste`](#parameter-bastionconfigurationdisablecopypaste) | bool | Choose to disable or enable Copy Paste. | +| [`enabled`](#parameter-bastionconfigurationenabled) | bool | Whether to create a Bastion host in the virtual network. Defaults to 'true'. | +| [`enableFileCopy`](#parameter-bastionconfigurationenablefilecopy) | bool | Choose to disable or enable File Copy. | +| [`enableIpConnect`](#parameter-bastionconfigurationenableipconnect) | bool | Choose to disable or enable IP Connect. | +| [`enableKerberos`](#parameter-bastionconfigurationenablekerberos) | bool | Choose to disable or enable Kerberos authentication. | +| [`enableShareableLink`](#parameter-bastionconfigurationenableshareablelink) | bool | Choose to disable or enable Shareable Link. | +| [`name`](#parameter-bastionconfigurationname) | string | The name of the Bastion host to create. | +| [`networkSecurityGroupResourceId`](#parameter-bastionconfigurationnetworksecuritygroupresourceid) | string | The resource ID of an existing network security group to associate with the Azure Bastion subnet. | +| [`scaleUnits`](#parameter-bastionconfigurationscaleunits) | int | The scale units for the Bastion Host resource. | +| [`sku`](#parameter-bastionconfigurationsku) | string | The SKU of the Bastion host to create. | +| [`subnetAddressPrefix`](#parameter-bastionconfigurationsubnetaddressprefix) | string | The address prefix of the Azure Bastion subnet. | + +### Parameter: `bastionConfiguration.disableCopyPaste` + +Choose to disable or enable Copy Paste. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.enabled` + +Whether to create a Bastion host in the virtual network. Defaults to 'true'. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.enableFileCopy` + +Choose to disable or enable File Copy. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.enableIpConnect` + +Choose to disable or enable IP Connect. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.enableKerberos` + +Choose to disable or enable Kerberos authentication. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.enableShareableLink` + +Choose to disable or enable Shareable Link. + +- Required: No +- Type: bool + +### Parameter: `bastionConfiguration.name` + +The name of the Bastion host to create. + +- Required: No +- Type: string + +### Parameter: `bastionConfiguration.networkSecurityGroupResourceId` + +The resource ID of an existing network security group to associate with the Azure Bastion subnet. + +- Required: No +- Type: string + +### Parameter: `bastionConfiguration.scaleUnits` + +The scale units for the Bastion Host resource. + +- Required: No +- Type: int + +### Parameter: `bastionConfiguration.sku` + +The SKU of the Bastion host to create. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Basic' + 'Standard' + ] + ``` + +### Parameter: `bastionConfiguration.subnetAddressPrefix` + +The address prefix of the Azure Bastion subnet. + +- Required: No +- Type: string + +### Parameter: `containerRegistryConfiguration` + +Configuration for the container registry. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-containerregistryconfigurationname) | string | The name of the container registry. | +| [`trustPolicyStatus`](#parameter-containerregistryconfigurationtrustpolicystatus) | string | Whether the trust policy is enabled for the container registry. Defaults to 'enabled'. | + +### Parameter: `containerRegistryConfiguration.name` + +The name of the container registry. + +- Required: No +- Type: string + +### Parameter: `containerRegistryConfiguration.trustPolicyStatus` + +Whether the trust policy is enabled for the container registry. Defaults to 'enabled'. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'disabled' + 'enabled' + ] + ``` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `keyVaultConfiguration` + +Configuration for the key vault. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enablePurgeProtection`](#parameter-keyvaultconfigurationenablepurgeprotection) | bool | Provide 'true' to enable Key Vault's purge protection feature. Defaults to 'true'. | +| [`name`](#parameter-keyvaultconfigurationname) | string | The name of the key vault. | + +### Parameter: `keyVaultConfiguration.enablePurgeProtection` + +Provide 'true' to enable Key Vault's purge protection feature. Defaults to 'true'. + +- Required: No +- Type: bool + +### Parameter: `keyVaultConfiguration.name` + +The name of the key vault. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `logAnalyticsConfiguration` + +Configuration for the Log Analytics workspace. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-loganalyticsconfigurationname) | string | The name of the Log Analytics workspace. | + +### Parameter: `logAnalyticsConfiguration.name` + +The name of the Log Analytics workspace. + +- Required: No +- Type: string + +### Parameter: `managedIdentityConfiguration` + +Configuration for the user-assigned managed identities. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`hubName`](#parameter-managedidentityconfigurationhubname) | string | The name of the workspace hub user-assigned managed identity. | +| [`projectName`](#parameter-managedidentityconfigurationprojectname) | string | The name of the workspace project user-assigned managed identity. | + +### Parameter: `managedIdentityConfiguration.hubName` + +The name of the workspace hub user-assigned managed identity. + +- Required: No +- Type: string + +### Parameter: `managedIdentityConfiguration.projectName` + +The name of the workspace project user-assigned managed identity. + +- Required: No +- Type: string + +### Parameter: `storageAccountConfiguration` + +Configuration for the storage account. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowSharedKeyAccess`](#parameter-storageaccountconfigurationallowsharedkeyaccess) | bool | 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 Microsoft Entra ID. Defaults to 'false'. | +| [`name`](#parameter-storageaccountconfigurationname) | string | The name of the storage account. | +| [`sku`](#parameter-storageaccountconfigurationsku) | string | Storage account SKU. Defaults to 'Standard_RAGZRS'. | + +### Parameter: `storageAccountConfiguration.allowSharedKeyAccess` + +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 Microsoft Entra ID. Defaults to 'false'. + +- Required: No +- Type: bool + +### Parameter: `storageAccountConfiguration.name` + +The name of the storage account. + +- Required: No +- Type: string + +### Parameter: `storageAccountConfiguration.sku` + +Storage account SKU. Defaults to 'Standard_RAGZRS'. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Premium_LRS' + 'Premium_ZRS' + 'Standard_GRS' + 'Standard_GZRS' + 'Standard_LRS' + 'Standard_RAGRS' + 'Standard_RAGZRS' + 'Standard_ZRS' + ] + ``` + +### Parameter: `tags` + +Resource tags. + +- Required: No +- Type: object + +### Parameter: `virtualMachineConfiguration` + +Configuration for the virtual machine. + +- Required: No +- Type: secureObject + +### Parameter: `virtualNetworkConfiguration` + +Configuration for the virtual network. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefix`](#parameter-virtualnetworkconfigurationaddressprefix) | string | The address prefix of the virtual network to create. | +| [`enabled`](#parameter-virtualnetworkconfigurationenabled) | bool | Whether to create an associated virtual network. Defaults to 'true'. | +| [`name`](#parameter-virtualnetworkconfigurationname) | string | The name of the virtual network to create. | +| [`subnet`](#parameter-virtualnetworkconfigurationsubnet) | object | Configuration for the virual network subnet. | + +### Parameter: `virtualNetworkConfiguration.addressPrefix` + +The address prefix of the virtual network to create. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkConfiguration.enabled` + +Whether to create an associated virtual network. Defaults to 'true'. + +- Required: No +- Type: bool + +### Parameter: `virtualNetworkConfiguration.name` + +The name of the virtual network to create. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkConfiguration.subnet` + +Configuration for the virual network subnet. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefix`](#parameter-virtualnetworkconfigurationsubnetaddressprefix) | string | The address prefix of the subnet to create. | +| [`name`](#parameter-virtualnetworkconfigurationsubnetname) | string | The name of the subnet to create. | +| [`networkSecurityGroupResourceId`](#parameter-virtualnetworkconfigurationsubnetnetworksecuritygroupresourceid) | string | The resource ID of an existing network security group to associate with the subnet. | + +### Parameter: `virtualNetworkConfiguration.subnet.addressPrefix` + +The address prefix of the subnet to create. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkConfiguration.subnet.name` + +The name of the subnet to create. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkConfiguration.subnet.networkSecurityGroupResourceId` + +The resource ID of an existing network security group to associate with the subnet. + +- Required: No +- Type: string + +### Parameter: `workspaceConfiguration` + +Configuration for the AI Studio workspace. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`computes`](#parameter-workspaceconfigurationcomputes) | array | Computes to create and attach to the workspace hub. | +| [`name`](#parameter-workspaceconfigurationname) | string | The name of the AI Studio workspace hub. | +| [`networkIsolationMode`](#parameter-workspaceconfigurationnetworkisolationmode) | string | The network isolation mode of the workspace hub. Defaults to 'AllowInternetOutbound'. | +| [`networkOutboundRules`](#parameter-workspaceconfigurationnetworkoutboundrules) | object | The outbound rules for the managed network of the workspace hub. | +| [`projectName`](#parameter-workspaceconfigurationprojectname) | string | The name of the AI Studio workspace project. | + +### Parameter: `workspaceConfiguration.computes` + +Computes to create and attach to the workspace hub. + +- Required: No +- Type: array + +### Parameter: `workspaceConfiguration.name` + +The name of the AI Studio workspace hub. + +- Required: No +- Type: string + +### Parameter: `workspaceConfiguration.networkIsolationMode` + +The network isolation mode of the workspace hub. Defaults to 'AllowInternetOutbound'. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AllowInternetOutbound' + 'AllowOnlyApprovedOutbound' + ] + ``` + +### Parameter: `workspaceConfiguration.networkOutboundRules` + +The outbound rules for the managed network of the workspace hub. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`>Any_other_property<`](#parameter-workspaceconfigurationnetworkoutboundrules>any_other_property<) | object | The outbound rule. The name of the rule is the object key. | + +### Parameter: `workspaceConfiguration.networkOutboundRules.>Any_other_property<` + +The outbound rule. The name of the rule is the object key. + +- Required: Yes +- Type: object + +### Parameter: `workspaceConfiguration.projectName` + +The name of the AI Studio workspace project. + +- Required: No +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `applicationInsightsApplicationId` | string | The application ID of the application insights component. | +| `applicationInsightsConnectionString` | string | The connection string of the application insights component. | +| `applicationInsightsInstrumentationKey` | string | The instrumentation key of the application insights component. | +| `applicationInsightsName` | string | The name of the application insights component. | +| `applicationInsightsResourceId` | string | The resource ID of the application insights component. | +| `bastionName` | string | The name of the Azure Bastion host. | +| `bastionResourceId` | string | The resource ID of the Azure Bastion host. | +| `containerRegistryName` | string | The name of the container registry. | +| `containerRegistryResourceId` | string | The resource ID of the container registry. | +| `keyVaultName` | string | The name of the key vault. | +| `keyVaultResourceId` | string | The resource ID of the key vault. | +| `keyVaultUri` | string | The URI of the key vault. | +| `location` | string | The location the module was deployed to. | +| `logAnalyticsWorkspaceName` | string | The name of the log analytics workspace. | +| `logAnalyticsWorkspaceResourceId` | string | The resource ID of the log analytics workspace. | +| `managedIdentityHubClientId` | string | The client ID of the workspace hub user assigned managed identity. | +| `managedIdentityHubName` | string | The name of the workspace hub user assigned managed identity. | +| `managedIdentityHubPrincipalId` | string | The principal ID of the workspace hub user assigned managed identity. | +| `managedIdentityHubResourceId` | string | The resource ID of the workspace hub user assigned managed identity. | +| `managedIdentityProjectClientId` | string | The client ID of the workspace project user assigned managed identity. | +| `managedIdentityProjectName` | string | The name of the workspace project user assigned managed identity. | +| `managedIdentityProjectPrincipalId` | string | The principal ID of the workspace project user assigned managed identity. | +| `managedIdentityProjectResourceId` | string | The resource ID of the workspace project user assigned managed identity. | +| `resourceGroupName` | string | The name of the resource group the module was deployed to. | +| `storageAccountName` | string | The name of the storage account. | +| `storageAccountResourceId` | string | The resource ID of the storage account. | +| `virtualMachineName` | string | The name of the virtual machine. | +| `virtualMachineResourceId` | string | The resource ID of the virtual machine. | +| `virtualNetworkName` | string | The name of the virtual network. | +| `virtualNetworkResourceId` | string | The resource ID of the virtual network. | +| `virtualNetworkSubnetName` | string | The name of the subnet in the virtual network. | +| `virtualNetworkSubnetResourceId` | string | The resource ID of the subnet in the virtual network. | +| `workspaceHubName` | string | The name of the workspace hub. | +| `workspaceHubResourceId` | string | The resource ID of the workspace hub. | +| `workspaceProjectName` | string | The name of the workspace project. | +| `workspaceProjectResourceId` | string | The resource ID of the workspace project. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/compute/virtual-machine:0.5.3` | Remote reference | +| `br/public:avm/res/container-registry/registry:0.3.1` | Remote reference | +| `br/public:avm/res/insights/component:0.3.1` | Remote reference | +| `br/public:avm/res/key-vault/vault:0.6.2` | Remote reference | +| `br/public:avm/res/machine-learning-services/workspace:0.5.0` | Remote reference | +| `br/public:avm/res/network/bastion-host:0.2.2` | Remote reference | +| `br/public:avm/res/network/network-security-group:0.3.1` | Remote reference | +| `br/public:avm/res/network/private-dns-zone:0.3.1` | Remote reference | +| `br/public:avm/res/storage/storage-account:0.11.0` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/ai-platform/baseline/main.bicep b/avm/ptn/ai-platform/baseline/main.bicep new file mode 100644 index 0000000000..d3095c0f79 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/main.bicep @@ -0,0 +1,981 @@ +metadata name = 'AI Platform Baseline' +metadata description = '''This module provides a secure and scalable environment for deploying AI applications on Azure. +The module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services. +By integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access.''' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Alphanumberic suffix to use for resource naming.') +@minLength(3) +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Resource tags.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Configuration for the user-assigned managed identities.') +param managedIdentityConfiguration managedIdentityConfigurationType + +@description('Optional. Configuration for the Log Analytics workspace.') +param logAnalyticsConfiguration logAnalyticsConfigurationType + +@description('Optional. Configuration for the key vault.') +param keyVaultConfiguration keyVaultConfigurationType + +@description('Optional. Configuration for the storage account.') +param storageAccountConfiguration storageAccountConfigurationType + +@description('Optional. Configuration for the container registry.') +param containerRegistryConfiguration containerRegistryConfigurationType + +@description('Optional. Configuration for Application Insights.') +param applicationInsightsConfiguration applicationInsightsConfigurationType + +@description('Optional. Configuration for the AI Studio workspace.') +param workspaceConfiguration workspaceConfigurationType + +@description('Optional. Configuration for the virtual network.') +param virtualNetworkConfiguration virtualNetworkConfigurationType + +@description('Optional. Configuration for the Azure Bastion host.') +param bastionConfiguration bastionConfigurationType + +@description('Optional. Configuration for the virtual machine.') +param virtualMachineConfiguration virtualMachineConfigurationType + +// ============== // +// Variables // +// ============== // + +var createVirtualNetwork = virtualNetworkConfiguration.?enabled != false + +var createBastion = createVirtualNetwork && bastionConfiguration.?enabled != false + +var createVirtualMachine = createVirtualNetwork && virtualMachineConfiguration.?enabled != false + +var createDefaultNsg = virtualNetworkConfiguration.?subnet.networkSecurityGroupResourceId == null + +var subnetResourceId = createVirtualNetwork ? virtualNetwork::defaultSubnet.id : null + +var mlTargetSubResource = 'amlworkspace' + +var mlPrivateDnsZones = { + 'privatelink.api.azureml.ms': mlTargetSubResource + 'privatelink.notebooks.azure.net': mlTargetSubResource +} + +var storagePrivateDnsZones = { + 'privatelink.blob.${environment().suffixes.storage}': 'blob' + 'privatelink.file.${environment().suffixes.storage}': 'file' +} + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.aiplatform-baseline.${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' + } + } + } + } +} + +module storageAccount_privateDnsZones 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ + for zone in objectKeys(storagePrivateDnsZones): if (createVirtualNetwork) { + name: '${uniqueString(deployment().name, location, zone)}-storage-private-dns-zones' + params: { + name: zone + virtualNetworkLinks: [ + { + virtualNetworkResourceId: virtualNetwork.id + } + ] + } + } +] + +module workspaceHub_privateDnsZones 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ + for zone in objectKeys(mlPrivateDnsZones): if (createVirtualNetwork) { + name: '${uniqueString(deployment().name, location, zone)}-workspace-private-dns-zones' + params: { + name: zone + virtualNetworkLinks: [ + { + virtualNetworkResourceId: virtualNetwork.id + } + ] + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + } + } +] + +module defaultNetworkSecurityGroup 'br/public:avm/res/network/network-security-group:0.3.1' = if (createDefaultNsg) { + name: '${uniqueString(deployment().name, location)}-nsg' + params: { + name: 'nsg-${name}' + location: location + securityRules: [ + { + name: 'DenySshRdpOutbound' + properties: { + priority: 200 + access: 'Deny' + protocol: '*' + direction: 'Outbound' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRanges: [ + '3389' + '22' + ] + } + } + ] + tags: tags + } +} + +// Not using the br/public:avm/res/network/virtual-network module here to +// allow consumers of the module to add subnets from outside of the module +// https://github.com/Azure/bicep-registry-modules/issues/2689 +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = if (createVirtualNetwork) { + name: virtualNetworkConfiguration.?name ?? 'vnet-${name}' + location: location + tags: tags + properties: { + addressSpace: { + addressPrefixes: [ + virtualNetworkConfiguration.?addressPrefix ?? '10.0.0.0/16' + ] + } + } + + resource defaultSubnet 'subnets@2024-01-01' = { + name: virtualNetworkConfiguration.?subnet.name ?? 'default' + properties: { + addressPrefix: virtualNetworkConfiguration.?subnet.addressPrefix ?? '10.0.0.0/24' + networkSecurityGroup: { + id: createDefaultNsg + ? defaultNetworkSecurityGroup.outputs.resourceId + : virtualNetworkConfiguration.?subnet.networkSecurityGroupResourceId + } + } + } + + resource bastionSubnet 'subnets@2024-01-01' = if (createBastion) { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: bastionConfiguration.?subnetAddressPrefix ?? '10.0.1.0/26' + networkSecurityGroup: bastionConfiguration.?networkSecurityGroupResourceId != null + ? { + id: bastionConfiguration.?networkSecurityGroupResourceId + } + : null + } + + dependsOn: [ + defaultSubnet + ] + } +} + +module bastion 'br/public:avm/res/network/bastion-host:0.2.2' = if (createBastion) { + name: '${uniqueString(deployment().name, location)}-bastion-host' + params: { + name: bastionConfiguration.?name ?? 'bas-${name}' + location: location + skuName: bastionConfiguration.?sku ?? 'Standard' + enableTelemetry: enableTelemetry + virtualNetworkResourceId: virtualNetwork.id + disableCopyPaste: bastionConfiguration.?disableCopyPaste + enableFileCopy: bastionConfiguration.?enableFileCopy + enableIpConnect: bastionConfiguration.?enableIpConnect + enableKerberos: bastionConfiguration.?enableKerberos + enableShareableLink: bastionConfiguration.?enableShareableLink + scaleUnits: bastionConfiguration.?scaleUnits + tags: tags + } +} + +module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.5.3' = if (createVirtualMachine) { + name: '${uniqueString(deployment().name, location)}-virtual-machine' + params: { + name: virtualMachineConfiguration.?name ?? 'vm-${name}' + computerName: virtualMachineConfiguration.?name ?? take('vm-${name}', 15) + location: location + enableTelemetry: enableTelemetry + adminUsername: virtualMachineConfiguration.?adminUsername ?? '' + adminPassword: virtualMachineConfiguration.?adminPassword + nicConfigurations: [ + { + name: virtualMachineConfiguration.?nicConfigurationConfiguration.name ?? 'nic-vm-${name}' + location: location + networkSecurityGroupResourceId: virtualMachineConfiguration.?nicConfigurationConfiguration.networkSecurityGroupResourceId + ipConfigurations: [ + { + name: virtualMachineConfiguration.?nicConfigurationConfiguration.ipConfigName ?? 'nic-vm-${name}-ipconfig' + privateIPAllocationMethod: virtualMachineConfiguration.?nicConfigurationConfiguration.privateIPAllocationMethod ?? 'Dynamic' + subnetResourceId: virtualNetwork::defaultSubnet.id + } + ] + } + ] + imageReference: virtualMachineConfiguration.?imageReference ?? { + publisher: 'microsoft-dsvm' + offer: 'dsvm-win-2022' + sku: 'winserver-2022' + version: 'latest' + } + osDisk: virtualMachineConfiguration.?osDisk ?? { + createOption: 'FromImage' + managedDisk: { + storageAccountType: 'Premium_ZRS' + } + diskSizeGB: 128 + caching: 'ReadWrite' + } + patchMode: virtualMachineConfiguration.?patchMode + osType: 'Windows' + encryptionAtHost: virtualMachineConfiguration.?encryptionAtHost ?? true + vmSize: virtualMachineConfiguration.?size ?? 'Standard_D2s_v3' + zone: virtualMachineConfiguration.?zone ?? 0 + extensionAadJoinConfig: virtualMachineConfiguration.?enableAadLoginExtension == true + ? { + enabled: true + typeHandlerVersion: '1.0' + } + : null + extensionMonitoringAgentConfig: virtualMachineConfiguration.?enableAzureMonitorAgent == true + ? { + enabled: true + } + : null + maintenanceConfigurationResourceId: virtualMachineConfiguration.?maintenanceConfigurationResourceId + tags: tags + } +} + +resource managedIdentityHub 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityConfiguration.?hubName ?? 'id-hub-${name}' + location: location + tags: tags +} + +resource managedIdentityProject 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityConfiguration.?projectName ?? 'id-project-${name}' + location: location + tags: tags +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: logAnalyticsConfiguration.?name ?? 'log-${name}' + location: location + tags: tags +} + +resource resourceGroup_roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, name) + properties: { + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' // Reader + ) + principalId: managedIdentityHub.properties.principalId + principalType: 'ServicePrincipal' + } +} + +module keyVault 'br/public:avm/res/key-vault/vault:0.6.2' = { + name: '${uniqueString(deployment().name, location)}-key-vault' + params: { + name: keyVaultConfiguration.?name ?? 'kv-${name}' + location: location + enableTelemetry: enableTelemetry + enableRbacAuthorization: true + enableVaultForDeployment: false + enableVaultForDiskEncryption: false + enableVaultForTemplateDeployment: false + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + } + publicNetworkAccess: 'Disabled' + enablePurgeProtection: keyVaultConfiguration.?enablePurgeProtection ?? true + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Key Vault Administrator' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Key Vault Administrator' + principalType: 'ServicePrincipal' + } + ] + diagnosticSettings: [ + { + workspaceResourceId: logAnalyticsWorkspace.id + logCategoriesAndGroups: [ + { + category: 'AuditEvent' + enabled: true + } + ] + } + ] + tags: tags + } +} + +module storageAccount 'br/public:avm/res/storage/storage-account:0.11.0' = { + name: '${uniqueString(deployment().name, location)}-storage' + params: { + name: storageAccountConfiguration.?name ?? 'st${name}' + location: location + skuName: storageAccountConfiguration.?sku ?? 'Standard_RAGZRS' + enableTelemetry: enableTelemetry + allowBlobPublicAccess: false + allowSharedKeyAccess: storageAccountConfiguration.?allowSharedKeyAccess ?? true + defaultToOAuthAuthentication: !(storageAccountConfiguration.?allowSharedKeyAccess ?? true) + publicNetworkAccess: 'Disabled' + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + } + privateEndpoints: subnetResourceId != null + ? map(items(storagePrivateDnsZones), zone => { + name: 'pep-${zone.value}-${name}' + customNetworkInterfaceName: 'nic-${zone.value}-${name}' + service: zone.value + subnetResourceId: subnetResourceId ?? '' + privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)] + }) + : null + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Reader' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Storage Account Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Storage Table Data Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor + principalType: 'ServicePrincipal' + } + ] + tags: tags + } + + dependsOn: storageAccount_privateDnsZones +} + +module containerRegistry 'br/public:avm/res/container-registry/registry:0.3.1' = { + name: '${uniqueString(deployment().name, location)}-container-registry' + params: { + name: containerRegistryConfiguration.?name ?? 'cr${name}' + acrSku: 'Premium' + location: location + enableTelemetry: enableTelemetry + publicNetworkAccess: 'Disabled' + networkRuleBypassOptions: 'AzureServices' + zoneRedundancy: 'Enabled' + trustPolicyStatus: containerRegistryConfiguration.?trustPolicyStatus ?? 'enabled' + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'AcrPull' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'AcrPull' + principalType: 'ServicePrincipal' + } + ] + tags: tags + } +} + +module applicationInsights 'br/public:avm/res/insights/component:0.3.1' = { + name: '${uniqueString(deployment().name, location)}-appi' + params: { + name: applicationInsightsConfiguration.?name ?? 'appi-${name}' + location: location + kind: 'web' + enableTelemetry: enableTelemetry + workspaceResourceId: logAnalyticsWorkspace.id + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + tags: tags + } +} + +module workspaceHub 'br/public:avm/res/machine-learning-services/workspace:0.5.0' = { + name: '${uniqueString(deployment().name, location)}-hub' + params: { + name: workspaceConfiguration.?name ?? 'hub-${name}' + sku: 'Standard' + location: location + enableTelemetry: enableTelemetry + kind: 'Hub' + associatedApplicationInsightsResourceId: applicationInsights.outputs.resourceId + associatedKeyVaultResourceId: keyVault.outputs.resourceId + associatedStorageAccountResourceId: storageAccount.outputs.resourceId + associatedContainerRegistryResourceId: containerRegistry.outputs.resourceId + workspaceHubConfig: { + defaultWorkspaceResourceGroup: resourceGroup().id + } + managedIdentities: { + userAssignedResourceIds: [ + managedIdentityHub.id + ] + } + primaryUserAssignedIdentity: managedIdentityHub.id + computes: workspaceConfiguration.?computes + managedNetworkSettings: { + isolationMode: workspaceConfiguration.?networkIsolationMode ?? 'AllowInternetOutbound' + outboundRules: workspaceConfiguration.?networkOutboundRules + } + privateEndpoints: subnetResourceId != null + ? [ + { + name: 'pep-${mlTargetSubResource}-${name}' + customNetworkInterfaceName: 'nic-${mlTargetSubResource}-${name}' + service: mlTargetSubResource + subnetResourceId: subnetResourceId ?? '' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: map(objectKeys(mlPrivateDnsZones), zone => { + name: replace(zone, '.', '-') + privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone) + }) + } + } + ] + : null + systemDatastoresAuthMode: 'identity' + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + tags: tags + } + + dependsOn: workspaceHub_privateDnsZones +} + +module workspaceProject 'br/public:avm/res/machine-learning-services/workspace:0.5.0' = { + name: '${uniqueString(deployment().name, location)}-project' + params: { + name: workspaceConfiguration.?projectName ?? 'project-${name}' + sku: 'Standard' + location: location + enableTelemetry: enableTelemetry + kind: 'Project' + hubResourceId: workspaceHub.outputs.resourceId + managedIdentities: { + userAssignedResourceIds: [ + managedIdentityProject.id + ] + } + primaryUserAssignedIdentity: managedIdentityProject.id + roleAssignments: [ + { + principalId: managedIdentityHub.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: managedIdentityProject.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + tags: tags + } +} + +// ============ // +// Outputs // +// ============ // + +@description('The name of the resource group the module was deployed to.') +output resourceGroupName string = resourceGroup().name + +@description('The location the module was deployed to.') +output location string = location + +@description('The resource ID of the application insights component.') +output applicationInsightsResourceId string = applicationInsights.outputs.resourceId + +@description('The name of the application insights component.') +output applicationInsightsName string = applicationInsights.outputs.name + +@description('The application ID of the application insights component.') +output applicationInsightsApplicationId string = applicationInsights.outputs.applicationId + +@description('The instrumentation key of the application insights component.') +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey + +@description('The connection string of the application insights component.') +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString + +@description('The resource ID of the log analytics workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The resource ID of the workspace hub user assigned managed identity.') +output managedIdentityHubResourceId string = managedIdentityHub.id + +@description('The name of the workspace hub user assigned managed identity.') +output managedIdentityHubName string = managedIdentityHub.name + +@description('The principal ID of the workspace hub user assigned managed identity.') +output managedIdentityHubPrincipalId string = managedIdentityHub.properties.principalId + +@description('The client ID of the workspace hub user assigned managed identity.') +output managedIdentityHubClientId string = managedIdentityHub.properties.clientId + +@description('The resource ID of the workspace project user assigned managed identity.') +output managedIdentityProjectResourceId string = managedIdentityProject.id + +@description('The name of the workspace project user assigned managed identity.') +output managedIdentityProjectName string = managedIdentityProject.name + +@description('The principal ID of the workspace project user assigned managed identity.') +output managedIdentityProjectPrincipalId string = managedIdentityProject.properties.principalId + +@description('The client ID of the workspace project user assigned managed identity.') +output managedIdentityProjectClientId string = managedIdentityProject.properties.clientId + +@description('The resource ID of the key vault.') +output keyVaultResourceId string = keyVault.outputs.resourceId + +@description('The name of the key vault.') +output keyVaultName string = keyVault.outputs.name + +@description('The URI of the key vault.') +output keyVaultUri string = keyVault.outputs.uri + +@description('The resource ID of the storage account.') +output storageAccountResourceId string = storageAccount.outputs.resourceId + +@description('The name of the storage account.') +output storageAccountName string = storageAccount.outputs.name + +@description('The resource ID of the container registry.') +output containerRegistryResourceId string = containerRegistry.outputs.resourceId + +@description('The name of the container registry.') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The resource ID of the workspace hub.') +output workspaceHubResourceId string = workspaceHub.outputs.resourceId + +@description('The name of the workspace hub.') +output workspaceHubName string = workspaceHub.outputs.name + +@description('The resource ID of the workspace project.') +output workspaceProjectResourceId string = workspaceProject.outputs.resourceId + +@description('The name of the workspace project.') +output workspaceProjectName string = workspaceProject.outputs.name + +@description('The resource ID of the virtual network.') +output virtualNetworkResourceId string = createVirtualNetwork ? virtualNetwork.id : '' + +@description('The name of the virtual network.') +output virtualNetworkName string = createVirtualNetwork ? virtualNetwork.name : '' + +@description('The resource ID of the subnet in the virtual network.') +output virtualNetworkSubnetResourceId string = createVirtualNetwork ? virtualNetwork::defaultSubnet.id : '' + +@description('The name of the subnet in the virtual network.') +output virtualNetworkSubnetName string = createVirtualNetwork ? virtualNetwork::defaultSubnet.name : '' + +@description('The resource ID of the Azure Bastion host.') +output bastionResourceId string = createBastion ? bastion.outputs.resourceId : '' + +@description('The name of the Azure Bastion host.') +output bastionName string = createBastion ? bastion.outputs.name : '' + +@description('The resource ID of the virtual machine.') +output virtualMachineResourceId string = createVirtualMachine ? virtualMachine.outputs.resourceId : '' + +@description('The name of the virtual machine.') +output virtualMachineName string = createVirtualMachine ? virtualMachine.outputs.name : '' + +// ================ // +// Definitions // +// ================ // + +type managedIdentityConfigurationType = { + @description('Optional. The name of the workspace hub user-assigned managed identity.') + hubName: string? + + @description('Optional. The name of the workspace project user-assigned managed identity.') + projectName: string? +}? + +type logAnalyticsConfigurationType = { + @description('Optional. The name of the Log Analytics workspace.') + name: string? +}? + +type keyVaultConfigurationType = { + @description('Optional. The name of the key vault.') + name: string? + + @description('Optional. Provide \'true\' to enable Key Vault\'s purge protection feature. Defaults to \'true\'.') + enablePurgeProtection: bool? +}? + +type storageAccountConfigurationType = { + @description('Optional. The name of the storage account.') + name: string? + + @description('Optional. Storage account SKU. Defaults to \'Standard_RAGZRS\'.') + sku: + | 'Standard_LRS' + | 'Standard_GRS' + | 'Standard_RAGRS' + | 'Standard_ZRS' + | 'Premium_LRS' + | 'Premium_ZRS' + | 'Standard_GZRS' + | 'Standard_RAGZRS'? + + @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 Microsoft Entra ID. Defaults to \'false\'.') + allowSharedKeyAccess: bool? +}? + +type containerRegistryConfigurationType = { + @description('Optional. The name of the container registry.') + name: string? + + @description('Optional. Whether the trust policy is enabled for the container registry. Defaults to \'enabled\'.') + trustPolicyStatus: 'enabled' | 'disabled'? +}? + +type applicationInsightsConfigurationType = { + @description('Optional. The name of the Application Insights resource.') + name: string? +}? + +type workspaceConfigurationType = { + @description('Optional. The name of the AI Studio workspace hub.') + name: string? + + @description('Optional. The name of the AI Studio workspace project.') + projectName: string? + + @description('Optional. Computes to create and attach to the workspace hub.') + computes: array? + + @description('Optional. The network isolation mode of the workspace hub. Defaults to \'AllowInternetOutbound\'.') + networkIsolationMode: 'AllowInternetOutbound' | 'AllowOnlyApprovedOutbound'? + + @description('Optional. The outbound rules for the managed network of the workspace hub.') + networkOutboundRules: networkOutboundRuleType +}? + +type virtualNetworkSubnetConfigurationType = { + @description('Optional. The name of the subnet to create.') + name: string? + + @description('Optional. The address prefix of the subnet to create.') + addressPrefix: string? + + @description('Optional. The resource ID of an existing network security group to associate with the subnet.') + networkSecurityGroupResourceId: string? +}? + +type virtualNetworkConfigurationType = { + @description('Optional. Whether to create an associated virtual network. Defaults to \'true\'.') + enabled: bool? + + @description('Optional. The name of the virtual network to create.') + name: string? + + @description('Optional. The address prefix of the virtual network to create.') + addressPrefix: string? + + @description('Optional. Configuration for the virual network subnet.') + subnet: virtualNetworkSubnetConfigurationType +}? + +type bastionConfigurationType = { + @description('Optional. Whether to create a Bastion host in the virtual network. Defaults to \'true\'.') + enabled: bool? + + @description('Optional. The name of the Bastion host to create.') + name: string? + + @description('Optional. The SKU of the Bastion host to create.') + sku: 'Basic' | 'Standard'? + + @description('Optional. The resource ID of an existing network security group to associate with the Azure Bastion subnet.') + networkSecurityGroupResourceId: string? + + @description('Optional. The address prefix of the Azure Bastion subnet.') + subnetAddressPrefix: string? + + @description('Optional. Choose to disable or enable Copy Paste.') + disableCopyPaste: bool? + + @description('Optional. Choose to disable or enable File Copy.') + enableFileCopy: bool? + + @description('Optional. Choose to disable or enable IP Connect.') + enableIpConnect: bool? + + @description('Optional. Choose to disable or enable Kerberos authentication.') + enableKerberos: bool? + + @description('Optional. Choose to disable or enable Shareable Link.') + enableShareableLink: bool? + + @description('Optional. The scale units for the Bastion Host resource.') + scaleUnits: int? +}? + +type nicConfigurationConfigurationType = { + @description('Optional. The name of the network interface.') + name: string? + + @description('Optional. The name of the IP configuration.') + ipConfigName: string? + + @description('Optional. The private IP address allocation method.') + privateIPAllocationMethod: 'Dynamic' | 'Static'? + + @description('Optional. The resource ID of an existing network security group to associate with the network interface.') + networkSecurityGroupResourceId: string? +}? + +type osDiskType = { + @description('Optional. The disk name.') + name: string? + + @description('Optional. Specifies the size of an empty data disk in gigabytes.') + diskSizeGB: int? + + @description('Optional. Specifies how the virtual machine should be created.') + createOption: 'Attach' | 'Empty' | 'FromImage'? + + @description('Optional. Specifies whether data disk should be deleted or detached upon VM deletion.') + deleteOption: 'Delete' | 'Detach'? + + @description('Optional. Specifies the caching requirements.') + caching: 'None' | 'ReadOnly' | 'ReadWrite'? + + @description('Required. The managed disk parameters.') + managedDisk: { + @description('Optional. Specifies the storage account type for the managed disk.') + storageAccountType: + | 'PremiumV2_LRS' + | 'Premium_LRS' + | 'Premium_ZRS' + | 'StandardSSD_LRS' + | 'StandardSSD_ZRS' + | 'Standard_LRS' + | 'UltraSSD_LRS'? + + @description('Optional. Specifies the customer managed disk encryption set resource id for the managed disk.') + diskEncryptionSetResourceId: string? + } +}? + +@secure() +type virtualMachineConfigurationType = { + @description('Optional. Whether to create a virtual machine in the associated virtual network. Defaults to \'true\'.') + enabled: bool? + + @description('Optional. The name of the virtual machine.') + @maxLength(15) + name: string? + + @description('Optional. The availability zone of the virtual machine. If set to 0, no availability zone is used (default).') + zone: 0 | 1 | 2 | 3? + + @description('Required. The virtual machine size. Defaults to \'Standard_D2s_v3\'.') + size: string? + + @description('Conditional. 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('Conditional. The password for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module.') + adminPassword: string? + + @description('Optional. Configuration for the virtual machine network interface.') + nicConfigurationConfiguration: nicConfigurationConfigurationType + + @description('Optional. 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.') + imageReference: object? + + @description('Optional. Specifies the OS disk.') + osDisk: osDiskType + + @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\'.') + encryptionAtHost: bool? + + @description('Optional. VM guest patching orchestration mode. Refer to \'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching\'.') + patchMode: 'AutomaticByPlatform' | 'AutomaticByOS' | 'Manual'? + + @description('Optional. Whether to enable the Microsoft.Azure.ActiveDirectory AADLoginForWindows extension, allowing users to log in to the virtual machine using Microsoft Entra. Defaults to \'false\'.') + enableAadLoginExtension: bool? + + @description('Optional. Whether to enable the Microsoft.Azure.Monitor AzureMonitorWindowsAgent extension. Defaults to \'false\'.') + enableAzureMonitorAgent: bool? + + @description('Optional. The resource Id of a maintenance configuration for the virtual machine.') + maintenanceConfigurationResourceId: string? +}? + +@discriminator('type') +type OutboundRuleType = FqdnOutboundRuleType | PrivateEndpointOutboundRule | ServiceTagOutboundRule + +type FqdnOutboundRuleType = { + @sys.description('Required. Type of a managed network Outbound Rule of the workspace hub. Only supported when \'isolationMode\' is \'AllowOnlyApprovedOutbound\'.') + type: 'FQDN' + + @sys.description('Required. Fully Qualified Domain Name to allow for outbound traffic.') + destination: string + + @sys.description('Optional. Category of a managed network Outbound Rule of the workspace hub.') + category: 'Dependency' | 'Recommended' | 'Required' | 'UserDefined'? +} + +type PrivateEndpointOutboundRule = { + @sys.description('Required. Type of a managed network Outbound Rule of the workspace hub.') + type: 'PrivateEndpoint' + + @sys.description('Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of the workspace hub.') + destination: { + @sys.description('Required. The resource ID of the target resource for the private endpoint.') + serviceResourceId: string + + @sys.description('Optional. Whether the private endpoint can be used by jobs running on Spark.') + sparkEnabled: bool? + + @sys.description('Required. The sub resource to connect for the private endpoint.') + subresourceTarget: string + } + + @sys.description('Optional. Category of a managed network Outbound Rule of the workspace hub.') + category: 'Dependency' | 'Recommended' | 'Required' | 'UserDefined'? +} + +type ServiceTagOutboundRule = { + @sys.description('Required. Type of a managed network Outbound Rule of the workspace hub. Only supported when \'isolationMode\' is \'AllowOnlyApprovedOutbound\'.') + type: 'ServiceTag' + + @sys.description('Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of the workspace hub.') + destination: { + @sys.description('Required. The name of the service tag to allow.') + portRanges: string + + @sys.description('Required. The protocol to allow. Provide an asterisk(*) to allow any protocol.') + protocol: 'TCP' | 'UDP' | 'ICMP' | '*' + + @sys.description('Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port.') + serviceTag: string + } + + @sys.description('Optional. Category of a managed network Outbound Rule of the workspace hub.') + category: 'Dependency' | 'Recommended' | 'Required' | 'UserDefined'? +} + +@sys.description('Optional. Outbound rules for the managed network of the workspace hub.') +type networkOutboundRuleType = { + @sys.description('Required. The outbound rule. The name of the rule is the object key.') + *: OutboundRuleType +}? diff --git a/avm/ptn/ai-platform/baseline/main.json b/avm/ptn/ai-platform/baseline/main.json new file mode 100644 index 0000000000..4fe65a2d83 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/main.json @@ -0,0 +1,29281 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2263138345896714301" + }, + "name": "AI Platform Baseline", + "description": "This module provides a secure and scalable environment for deploying AI applications on Azure.\nThe module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services.\nBy integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentityConfigurationType": { + "type": "object", + "properties": { + "hubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the workspace hub user-assigned managed identity." + } + }, + "projectName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the workspace project user-assigned managed identity." + } + } + }, + "nullable": true + }, + "logAnalyticsConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Log Analytics workspace." + } + } + }, + "nullable": true + }, + "keyVaultConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the key vault." + } + }, + "enablePurgeProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Provide 'true' to enable Key Vault's purge protection feature. Defaults to 'true'." + } + } + }, + "nullable": true + }, + "storageAccountConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the storage account." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS", + "Standard_GRS", + "Standard_GZRS", + "Standard_LRS", + "Standard_RAGRS", + "Standard_RAGZRS", + "Standard_ZRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Storage account SKU. Defaults to 'Standard_RAGZRS'." + } + }, + "allowSharedKeyAccess": { + "type": "bool", + "nullable": 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 Microsoft Entra ID. Defaults to 'false'." + } + } + }, + "nullable": true + }, + "containerRegistryConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the container registry." + } + }, + "trustPolicyStatus": { + "type": "string", + "allowedValues": [ + "disabled", + "enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Whether the trust policy is enabled for the container registry. Defaults to 'enabled'." + } + } + }, + "nullable": true + }, + "applicationInsightsConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Application Insights resource." + } + } + }, + "nullable": true + }, + "workspaceConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the AI Studio workspace hub." + } + }, + "projectName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the AI Studio workspace project." + } + }, + "computes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Computes to create and attach to the workspace hub." + } + }, + "networkIsolationMode": { + "type": "string", + "allowedValues": [ + "AllowInternetOutbound", + "AllowOnlyApprovedOutbound" + ], + "nullable": true, + "metadata": { + "description": "Optional. The network isolation mode of the workspace hub. Defaults to 'AllowInternetOutbound'." + } + }, + "networkOutboundRules": { + "$ref": "#/definitions/networkOutboundRuleType", + "metadata": { + "description": "Optional. The outbound rules for the managed network of the workspace hub." + } + } + }, + "nullable": true + }, + "virtualNetworkSubnetConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the subnet to create." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The address prefix of the subnet to create." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of an existing network security group to associate with the subnet." + } + } + }, + "nullable": true + }, + "virtualNetworkConfigurationType": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to create an associated virtual network. Defaults to 'true'." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the virtual network to create." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The address prefix of the virtual network to create." + } + }, + "subnet": { + "$ref": "#/definitions/virtualNetworkSubnetConfigurationType", + "metadata": { + "description": "Optional. Configuration for the virual network subnet." + } + } + }, + "nullable": true + }, + "bastionConfigurationType": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to create a Bastion host in the virtual network. Defaults to 'true'." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Bastion host to create." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Basic", + "Standard" + ], + "nullable": true, + "metadata": { + "description": "Optional. The SKU of the Bastion host to create." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of an existing network security group to associate with the Azure Bastion subnet." + } + }, + "subnetAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The address prefix of the Azure Bastion subnet." + } + }, + "disableCopyPaste": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Choose to disable or enable Copy Paste." + } + }, + "enableFileCopy": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy." + } + }, + "enableIpConnect": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect." + } + }, + "enableKerberos": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication." + } + }, + "enableShareableLink": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link." + } + }, + "scaleUnits": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource." + } + } + }, + "nullable": true + }, + "nicConfigurationConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the network interface." + } + }, + "ipConfigName": { + "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." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of an existing network security group to associate with the network interface." + } + } + }, + "nullable": true + }, + "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." + } + }, + "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." + } + } + }, + "nullable": true + }, + "virtualMachineConfigurationType": { + "type": "secureObject", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to create a virtual machine in the associated virtual network. Defaults to 'true'." + } + }, + "name": { + "type": "string", + "nullable": true, + "maxLength": 15, + "metadata": { + "description": "Optional. The name of the virtual machine." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "nullable": true, + "metadata": { + "description": "Optional. The availability zone of the virtual machine. If set to 0, no availability zone is used (default)." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The virtual machine size. Defaults to 'Standard_D2s_v3'." + } + }, + "adminUsername": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The username for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module." + } + }, + "adminPassword": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The password for the administrator account on the virtual machine. Required if a virtual machine is created as part of the module." + } + }, + "nicConfigurationConfiguration": { + "$ref": "#/definitions/nicConfigurationConfigurationType", + "metadata": { + "description": "Optional. Configuration for the virtual machine network interface." + } + }, + "imageReference": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. 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." + } + }, + "osDisk": { + "$ref": "#/definitions/osDiskType", + "metadata": { + "description": "Optional. Specifies the OS disk." + } + }, + "encryptionAtHost": { + "type": "bool", + "nullable": 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'." + } + }, + "patchMode": { + "type": "string", + "allowedValues": [ + "AutomaticByOS", + "AutomaticByPlatform", + "Manual" + ], + "nullable": true, + "metadata": { + "description": "Optional. VM guest patching orchestration mode. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'." + } + }, + "enableAadLoginExtension": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable the Microsoft.Azure.ActiveDirectory AADLoginForWindows extension, allowing users to log in to the virtual machine using Microsoft Entra. Defaults to 'false'." + } + }, + "enableAzureMonitorAgent": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable the Microsoft.Azure.Monitor AzureMonitorWindowsAgent extension. Defaults to 'false'." + } + }, + "maintenanceConfigurationResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource Id of a maintenance configuration for the virtual machine." + } + } + }, + "nullable": true + }, + "OutboundRuleType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "FQDN": { + "$ref": "#/definitions/FqdnOutboundRuleType" + }, + "PrivateEndpoint": { + "$ref": "#/definitions/PrivateEndpointOutboundRule" + }, + "ServiceTag": { + "$ref": "#/definitions/ServiceTagOutboundRule" + } + } + } + }, + "FqdnOutboundRuleType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "FQDN" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of the workspace hub. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "string", + "metadata": { + "description": "Required. Fully Qualified Domain Name to allow for outbound traffic." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of the workspace hub." + } + } + } + }, + "PrivateEndpointOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "PrivateEndpoint" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of the workspace hub." + } + }, + "destination": { + "type": "object", + "properties": { + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target resource for the private endpoint." + } + }, + "sparkEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the private endpoint can be used by jobs running on Spark." + } + }, + "subresourceTarget": { + "type": "string", + "metadata": { + "description": "Required. The sub resource to connect for the private endpoint." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of the workspace hub." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of the workspace hub." + } + } + } + }, + "ServiceTagOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "ServiceTag" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of the workspace hub. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "object", + "properties": { + "portRanges": { + "type": "string", + "metadata": { + "description": "Required. The name of the service tag to allow." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. The protocol to allow. Provide an asterisk(*) to allow any protocol." + } + }, + "serviceTag": { + "type": "string", + "metadata": { + "description": "Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of the workspace hub." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of the workspace hub." + } + } + } + }, + "networkOutboundRuleType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/OutboundRuleType", + "metadata": { + "description": "Required. The outbound rule. The name of the rule is the object key." + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules for the managed network of the workspace hub." + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 3, + "metadata": { + "description": "Required. Alphanumberic suffix to use for resource naming." + } + }, + "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." + } + }, + "managedIdentityConfiguration": { + "$ref": "#/definitions/managedIdentityConfigurationType", + "metadata": { + "description": "Optional. Configuration for the user-assigned managed identities." + } + }, + "logAnalyticsConfiguration": { + "$ref": "#/definitions/logAnalyticsConfigurationType", + "metadata": { + "description": "Optional. Configuration for the Log Analytics workspace." + } + }, + "keyVaultConfiguration": { + "$ref": "#/definitions/keyVaultConfigurationType", + "metadata": { + "description": "Optional. Configuration for the key vault." + } + }, + "storageAccountConfiguration": { + "$ref": "#/definitions/storageAccountConfigurationType", + "metadata": { + "description": "Optional. Configuration for the storage account." + } + }, + "containerRegistryConfiguration": { + "$ref": "#/definitions/containerRegistryConfigurationType", + "metadata": { + "description": "Optional. Configuration for the container registry." + } + }, + "applicationInsightsConfiguration": { + "$ref": "#/definitions/applicationInsightsConfigurationType", + "metadata": { + "description": "Optional. Configuration for Application Insights." + } + }, + "workspaceConfiguration": { + "$ref": "#/definitions/workspaceConfigurationType", + "metadata": { + "description": "Optional. Configuration for the AI Studio workspace." + } + }, + "virtualNetworkConfiguration": { + "$ref": "#/definitions/virtualNetworkConfigurationType", + "metadata": { + "description": "Optional. Configuration for the virtual network." + } + }, + "bastionConfiguration": { + "$ref": "#/definitions/bastionConfigurationType", + "metadata": { + "description": "Optional. Configuration for the Azure Bastion host." + } + }, + "virtualMachineConfiguration": { + "$ref": "#/definitions/virtualMachineConfigurationType", + "metadata": { + "description": "Optional. Configuration for the virtual machine." + } + } + }, + "variables": { + "createVirtualNetwork": "[not(equals(tryGet(parameters('virtualNetworkConfiguration'), 'enabled'), false()))]", + "createBastion": "[and(variables('createVirtualNetwork'), not(equals(tryGet(parameters('bastionConfiguration'), 'enabled'), false())))]", + "createVirtualMachine": "[and(variables('createVirtualNetwork'), not(equals(tryGet(parameters('virtualMachineConfiguration'), 'enabled'), false())))]", + "createDefaultNsg": "[equals(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'networkSecurityGroupResourceId'), null())]", + "subnetResourceId": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default')), null())]", + "mlTargetSubResource": "amlworkspace", + "mlPrivateDnsZones": { + "privatelink.api.azureml.ms": "[variables('mlTargetSubResource')]", + "privatelink.notebooks.azure.net": "[variables('mlTargetSubResource')]" + }, + "storagePrivateDnsZones": { + "[format('privatelink.blob.{0}', environment().suffixes.storage)]": "blob", + "[format('privatelink.file.{0}', environment().suffixes.storage)]": "file" + } + }, + "resources": { + "virtualNetwork::defaultSubnet": { + "condition": "[variables('createVirtualNetwork')]", + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'))]", + "properties": { + "addressPrefix": "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'addressPrefix'), '10.0.0.0/24')]", + "networkSecurityGroup": { + "id": "[if(variables('createDefaultNsg'), reference('defaultNetworkSecurityGroup').outputs.resourceId.value, tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'networkSecurityGroupResourceId'))]" + } + }, + "dependsOn": [ + "defaultNetworkSecurityGroup", + "virtualNetwork" + ] + }, + "virtualNetwork::bastionSubnet": { + "condition": "[and(variables('createVirtualNetwork'), variables('createBastion'))]", + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), 'AzureBastionSubnet')]", + "properties": { + "addressPrefix": "[coalesce(tryGet(parameters('bastionConfiguration'), 'subnetAddressPrefix'), '10.0.1.0/26')]", + "networkSecurityGroup": "[if(not(equals(tryGet(parameters('bastionConfiguration'), 'networkSecurityGroupResourceId'), null())), createObject('id', tryGet(parameters('bastionConfiguration'), 'networkSecurityGroupResourceId')), null())]" + }, + "dependsOn": [ + "virtualNetwork::defaultSubnet", + "virtualNetwork" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.aiplatform-baseline.{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" + } + } + } + } + }, + "virtualNetwork": { + "condition": "[variables('createVirtualNetwork')]", + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name')))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'addressPrefix'), '10.0.0.0/16')]" + ] + } + } + }, + "managedIdentityHub": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name')))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "managedIdentityProject": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name')))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "logAnalyticsWorkspace": { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2023-09-01", + "name": "[coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name')))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "resourceGroup_roleAssignment": { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(resourceGroup().id, parameters('name'))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "principalId": "[reference('managedIdentityHub').principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "managedIdentityHub" + ] + }, + "storageAccount_privateDnsZones": { + "copy": { + "name": "storageAccount_privateDnsZones", + "count": "[length(objectKeys(variables('storagePrivateDnsZones')))]" + }, + "condition": "[variables('createVirtualNetwork')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-storage-private-dns-zones', uniqueString(deployment().name, parameters('location'), objectKeys(variables('storagePrivateDnsZones'))[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[objectKeys(variables('storagePrivateDnsZones'))[copyIndex()]]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('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.28.1.47646", + "templateHash": "13567552508326841182" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{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" + } + } + } + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8657463061873174631" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1725749611610769138" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "12750497126800708872" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "13512848802819943036" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "5069219418696231980" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8649693365938652763" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1580431299497536987" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1628766837655887758" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8423159394395901832" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "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', '2020-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" + ] + }, + "workspaceHub_privateDnsZones": { + "copy": { + "name": "workspaceHub_privateDnsZones", + "count": "[length(objectKeys(variables('mlPrivateDnsZones')))]" + }, + "condition": "[variables('createVirtualNetwork')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspace-private-dns-zones', uniqueString(deployment().name, parameters('location'), objectKeys(variables('mlPrivateDnsZones'))[copyIndex()]))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[objectKeys(variables('mlPrivateDnsZones'))[copyIndex()]]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))))]" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "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.28.1.47646", + "templateHash": "13567552508326841182" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{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" + } + } + } + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8657463061873174631" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1725749611610769138" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "12750497126800708872" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "13512848802819943036" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "5069219418696231980" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "8649693365938652763" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1580431299497536987" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "1628766837655887758" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8423159394395901832" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "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', '2020-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": [ + "managedIdentityHub", + "virtualNetwork" + ] + }, + "defaultNetworkSecurityGroup": { + "condition": "[variables('createDefaultNsg')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-nsg', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('nsg-{0}', parameters('name'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "securityRules": { + "value": [ + { + "name": "DenySshRdpOutbound", + "properties": { + "priority": 200, + "access": "Deny", + "protocol": "*", + "direction": "Outbound", + "sourceAddressPrefix": "VirtualNetwork", + "sourcePortRange": "*", + "destinationAddressPrefix": "*", + "destinationPortRanges": [ + "3389", + "22" + ] + } + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2832086720057246360" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG).", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "securityRulesType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/securityRulesType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "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 of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{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" + } + } + } + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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]" + } + } + } + } + }, + "bastion": { + "condition": "[variables('createBastion')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-bastion-host', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('bastionConfiguration'), 'name'), format('bas-{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "skuName": { + "value": "[coalesce(tryGet(parameters('bastionConfiguration'), 'sku'), 'Standard')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "virtualNetworkResourceId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))))]" + }, + "disableCopyPaste": { + "value": "[tryGet(parameters('bastionConfiguration'), 'disableCopyPaste')]" + }, + "enableFileCopy": { + "value": "[tryGet(parameters('bastionConfiguration'), 'enableFileCopy')]" + }, + "enableIpConnect": { + "value": "[tryGet(parameters('bastionConfiguration'), 'enableIpConnect')]" + }, + "enableKerberos": { + "value": "[tryGet(parameters('bastionConfiguration'), 'enableKerberos')]" + }, + "enableShareableLink": { + "value": "[tryGet(parameters('bastionConfiguration'), 'enableShareableLink')]" + }, + "scaleUnits": { + "value": "[tryGet(parameters('bastionConfiguration'), 'scaleUnits')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8529247583922540645" + }, + "name": "Bastion Hosts", + "description": "This module deploys a Bastion Host.", + "owner": "Azure/module-maintainers" + }, + "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." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "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." + } + }, + "enableFileCopy": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy." + } + }, + "enableIpConnect": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect." + } + }, + "enableKerberos": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication." + } + }, + "enableShareableLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link." + } + }, + "scaleUnits": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.2.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" + } + } + } + } + }, + "azureBastion": { + "type": "Microsoft.Network/bastionHosts", + "apiVersion": "2022-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "properties": "[union(createObject('scaleUnits', if(equals(parameters('skuName'), 'Basic'), 2, parameters('scaleUnits')), 'ipConfigurations', createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value)))))), 'enableKerberos', parameters('enableKerberos')), if(equals(parameters('skuName'), 'Standard'), createObject('enableTunneling', equals(parameters('skuName'), 'Standard'), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), 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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/bastionHosts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "publicIPAddress": { + "condition": "[empty(parameters('bastionSubnetPublicIpResourceId'))]", + "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": "[parameters('enableTelemetry')]" + }, + "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": "[tryGet(parameters('publicIPAddressObject'), 'zones')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4718335757080871925" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "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." + } + } + }, + "metadata": { + "description": "Required. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{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" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-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": null + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-09-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', '2022-11-01', 'full').location]" + }, + "ipConfAzureBastionSubnet": { + "type": "object", + "metadata": { + "description": "The Public IPconfiguration object for the AzureBastionSubnet." + }, + "value": "[reference('azureBastion').ipConfigurations[0]]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualMachine": { + "condition": "[variables('createVirtualMachine')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtual-machine', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'name'), format('vm-{0}', parameters('name')))]" + }, + "computerName": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'name'), take(format('vm-{0}', parameters('name')), 15))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "adminUsername": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'adminUsername'), '')]" + }, + "adminPassword": { + "value": "[tryGet(parameters('virtualMachineConfiguration'), 'adminPassword')]" + }, + "nicConfigurations": { + "value": [ + { + "name": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'name'), format('nic-vm-{0}', parameters('name')))]", + "location": "[parameters('location')]", + "networkSecurityGroupResourceId": "[tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'networkSecurityGroupResourceId')]", + "ipConfigurations": [ + { + "name": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'ipConfigName'), format('nic-vm-{0}-ipconfig', parameters('name')))]", + "privateIPAllocationMethod": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'privateIPAllocationMethod'), 'Dynamic')]", + "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'))]" + } + ] + } + ] + }, + "imageReference": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'imageReference'), createObject('publisher', 'microsoft-dsvm', 'offer', 'dsvm-win-2022', 'sku', 'winserver-2022', 'version', 'latest'))]" + }, + "osDisk": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'osDisk'), createObject('createOption', 'FromImage', 'managedDisk', createObject('storageAccountType', 'Premium_ZRS'), 'diskSizeGB', 128, 'caching', 'ReadWrite'))]" + }, + "patchMode": { + "value": "[tryGet(parameters('virtualMachineConfiguration'), 'patchMode')]" + }, + "osType": { + "value": "Windows" + }, + "encryptionAtHost": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'encryptionAtHost'), true())]" + }, + "vmSize": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'size'), 'Standard_D2s_v3')]" + }, + "zone": { + "value": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'zone'), 0)]" + }, + "extensionAadJoinConfig": "[if(equals(tryGet(parameters('virtualMachineConfiguration'), 'enableAadLoginExtension'), true()), createObject('value', createObject('enabled', true(), 'typeHandlerVersion', '1.0')), createObject('value', null()))]", + "extensionMonitoringAgentConfig": "[if(equals(tryGet(parameters('virtualMachineConfiguration'), 'enableAzureMonitorAgent'), true()), createObject('value', createObject('enabled', true())), createObject('value', null()))]", + "maintenanceConfigurationResourceId": { + "value": "[tryGet(parameters('virtualMachineConfiguration'), 'maintenanceConfigurationResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2039205284269766938" + }, + "name": "Virtual Machines", + "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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." + } + }, + "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." + } + } + } + }, + "dataDisksType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "lun": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the logical unit number of the data disk." + } + }, + "diskSizeGB": { + "type": "int", + "metadata": { + "description": "Required. 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." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "nullable": true + } + }, + "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": "", + "metadata": { + "description": "Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings." + } + }, + "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": { + "type": "object", + "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": { + "type": "object", + "defaultValue": {}, + "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": { + "$ref": "#/definitions/dataDisksType", + "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." + } + }, + "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." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies set of certificates that should be installed onto the virtual machine." + } + }, + "priority": { + "type": "string", + "defaultValue": "Regular", + "allowedValues": [ + "Regular", + "Low", + "Spot" + ], + "metadata": { + "description": "Optional. Specifies the priority for the virtual machine." + } + }, + "enableEvictionPolicy": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of SSH public keys used to authenticate with linux based VMs." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the gallery applications that should be made available to the VM/VMSS." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set." + } + }, + "nicConfigurations": { + "type": "array", + "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": { + "type": "object", + "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": { + "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 + }, + "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": "object", + "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 identy." + } + }, + "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 identy." + } + }, + "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", + "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 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." + } + }, + "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", + "defaultValue": [], + "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." + } + }, + "winRM": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object." + } + }, + "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." + } + } + }, + "variables": { + "copy": [ + { + "name": "publicKeysFormatted", + "count": "[length(parameters('publicKeys'))]", + "input": { + "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", + "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" + } + } + ], + "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'), '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(), parameters('additionalUnattendContent'))]", + "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), 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 (Preview)": "[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.5.3', '.', '-'), 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" + } + } + } + } + }, + "vm": { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "zones": "[if(not(equals(parameters('zone'), 0)), array(string(parameters('zone'))), null())]", + "plan": "[if(not(empty(parameters('plan'))), parameters('plan'), null())]", + "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": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))]", + "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].diskSizeGB]", + "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty')]", + "deleteOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete')]", + "caching": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + } + ], + "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')]", + "diskSizeGB": "[parameters('osDisk').diskSizeGB]", + "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[parameters('osDisk').managedDisk.storageAccountType]", + "diskEncryptionSet": { + "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + }, + "additionalCapabilities": { + "ultraSSDEnabled": "[parameters('ultraSSDEnabled')]" + }, + "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": "[if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].deleteOption, 'Delete')]", + "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]" + }, + "id": "[resourceId('Microsoft.Network/networkInterfaces', if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].name, format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].nicSuffix)))]" + } + } + ] + }, + "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(parameters('enableEvictionPolicy'), 'Deallocate', 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())]" + }, + "dependsOn": [ + "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": "[if(contains(parameters('autoShutdownConfig'), 'status'), parameters('autoShutdownConfig').status, 'Disabled')]", + "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]", + "taskType": "ComputeVmShutdownTask", + "dailyRecurrence": { + "time": "[if(contains(parameters('autoShutdownConfig'), 'dailyRecurrenceTime'), parameters('autoShutdownConfig').dailyRecurrenceTime, '19:00')]" + }, + "timeZoneId": "[if(contains(parameters('autoShutdownConfig'), 'timeZone'), parameters('autoShutdownConfig').timeZone, 'UTC')]", + "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), createObject('status', if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), parameters('autoShutdownConfig').notificationStatus, 'Disabled'), 'emailRecipient', if(contains(parameters('autoShutdownConfig'), 'notificationEmail'), parameters('autoShutdownConfig').notificationEmail, ''), 'notificationLocale', if(contains(parameters('autoShutdownConfig'), 'notificationLocale'), parameters('autoShutdownConfig').notificationLocale, 'en'), 'webhookUrl', if(contains(parameters('autoShutdownConfig'), 'notificationWebhookUrl'), parameters('autoShutdownConfig').notificationWebhookUrl, ''), 'timeInMinutes', if(contains(parameters('autoShutdownConfig'), 'notificationTimeInMinutes'), parameters('autoShutdownConfig').notificationTimeInMinutes, 30)), null())]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_logAnalyticsWorkspace": { + "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "subscriptionId": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]]", + "name": "[last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))]" + }, + "AzureWindowsBaseline": { + "condition": "[not(empty(parameters('guestConfiguration')))]", + "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments", + "apiVersion": "2020-06-25", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'name'), createObject('value', parameters('nicConfigurations')[copyIndex()].name), createObject('value', format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex()].nicSuffix)))]", + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableIPForwarding": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableIPForwarding), createObject('value', false()))]", + "enableAcceleratedNetworking": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableAcceleratedNetworking), createObject('value', true()))]", + "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(parameters('nicConfigurations')[copyIndex()].dnsServers)), createObject('value', parameters('nicConfigurations')[copyIndex()].dnsServers), createObject('value', createArray())), createObject('value', createArray()))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('nicConfigurations')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "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": "[coalesce(tryGet(parameters('nicConfigurations')[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.28.1.47646", + "templateHash": "6185170122493883924" + } + }, + "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." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "networkInterfaceName": { + "type": "string" + }, + "virtualMachineName": { + "type": "string" + }, + "ipConfigurations": { + "type": "array" + }, + "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", + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the Network Interface." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "networkInterface_publicIPAddresses": { + "copy": { + "name": "networkInterface_publicIPAddresses", + "count": "[length(parameters('ipConfigurations'))]" + }, + "condition": "[contains(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'name'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.name), createObject('value', format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIpNameSuffix)))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "idleTimeoutInMinutes": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'idleTimeoutInMinutes')]" + }, + "ddosSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'ddosSettings')]" + }, + "dnsSettings": { + "value": "[tryGet(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'dnsSettings')]" + }, + "publicIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAddressVersion'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAddressVersion), createObject('value', 'IPv4'))]", + "publicIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAllocationMethod'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAllocationMethod), createObject('value', 'Static'))]", + "publicIpPrefixResourceId": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPPrefixResourceId'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPPrefixResourceId), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'roleAssignments'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.roleAssignments), createObject('value', createArray()))]", + "skuName": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuName'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuName), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuTier'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuTier), createObject('value', 'Regional'))]", + "tags": { + "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "zones": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'zones'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.zones), createObject('value', createArray(1, 2, 3)))]", + "enableTelemetry": { + "value": "[coalesce(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.27.1.19265", + "templateHash": "10356333973104369631" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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" + ], + "metadata": { + "description": "Required. 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." + } + } + } + }, + "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." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "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": { + "$ref": "#/definitions/roleAssignmentType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{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" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-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": null + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-09-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', if(not(empty(parameters('ipConfigurations')[copyIndex('value')].name)), parameters('ipConfigurations')[copyIndex('value')].name, null()), 'primary', equals(copyIndex('value'), 0), 'privateIPAllocationMethod', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod, null()), null()), 'privateIPAddress', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('value')].privateIPAddress, null()), null()), 'publicIPAddressResourceId', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), resourceId('Microsoft.Network/publicIPAddresses', if(contains(parameters('ipConfigurations')[copyIndex('value')].pipConfiguration, 'name'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.name, format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.publicIpNameSuffix))), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerBackendAddressPools, null()), 'applicationSecurityGroups', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('value')].applicationSecurityGroups, null()), 'applicationGatewayBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].applicationGatewayBackendAddressPools, null()), 'gatewayLoadBalancer', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('value')].gatewayLoadBalancer, null()), 'loadBalancerInboundNatRules', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerInboundNatRules, null()), 'privateIPAddressVersion', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('value')].privateIPAddressVersion, null()), 'virtualNetworkTaps', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('value')].virtualNetworkTaps, null()))]" + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": "[parameters('diagnosticSettings')]" + }, + "dnsServers": "[if(not(empty(parameters('dnsServers'))), createObject('value', parameters('dnsServers')), createObject('value', createArray()))]", + "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": "[if(not(empty(parameters('roleAssignments'))), createObject('value', parameters('roleAssignments')), 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.25.53.49325", + "templateHash": "1612343535299711142" + }, + "name": "Network Interface", + "description": "This module deploys a Network Interface.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + } + }, + "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", + "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", + "metadata": { + "description": "Required. A list of IPConfigurations of the network interface." + } + }, + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.2.4', '.', '-'), 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": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "ipConfigurations", + "count": "[length(parameters('ipConfigurations'))]", + "input": { + "name": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'name'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].name, format('ipconfig0{0}', add(copyIndex('ipConfigurations'), 1)))]", + "properties": { + "primary": "[if(equals(copyIndex('ipConfigurations'), 0), true(), false())]", + "privateIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAllocationMethod, null()), null())]", + "privateIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddress, null()), null())]", + "publicIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), if(not(equals(parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId, null())), createObject('id', parameters('ipConfigurations')[copyIndex('ipConfigurations')].publicIPAddressResourceId), null()), null())]", + "subnet": { + "id": "[parameters('ipConfigurations')[copyIndex('ipConfigurations')].subnetResourceId]" + }, + "loadBalancerBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerBackendAddressPools, null())]", + "applicationSecurityGroups": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationSecurityGroups, null())]", + "applicationGatewayBackendAddressPools": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].applicationGatewayBackendAddressPools, null())]", + "gatewayLoadBalancer": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].gatewayLoadBalancer, null())]", + "loadBalancerInboundNatRules": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].loadBalancerInboundNatRules, null())]", + "privateIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].privateIPAddressVersion, null())]", + "virtualNetworkTaps": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('ipConfigurations')].virtualNetworkTaps, null())]" + } + } + } + ], + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkInterfaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "networkInterface_publicIPAddresses" + ] + } + } + } + } + }, + "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": "AADLogin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.ActiveDirectory" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AADLoginForWindows'), createObject('value', 'AADSSHLoginforLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAadJoinConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAadJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAadJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAadJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAadJoinConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionAadJoinConfig'), 'settings'), createObject('value', parameters('extensionAadJoinConfig').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": { + "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": "DomainJoin" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Compute" + }, + "type": { + "value": "JsonADDomainExtension" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDomainJoinConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDomainJoinConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDomainJoinConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDomainJoinConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDomainJoinConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "MicrosoftAntiMalware" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": { + "value": "IaaSAntimalware" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAntiMalwareConfig').typeHandlerVersion), createObject('value', '1.3'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAntiMalwareConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAntiMalwareConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAntiMalwareConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAntiMalwareConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": { + "value": "[parameters('extensionAntiMalwareConfig').settings]" + }, + "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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_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": "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())]" + }, + "settings": { + "value": { + "workspaceId": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), reference('vm_logAnalyticsWorkspace').customerId, '')]", + "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" + } + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": { + "workspaceKey": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2], split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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_logAnalyticsWorkspace", + "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": "DependencyAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Monitoring.DependencyAgent" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDependencyAgentConfig').typeHandlerVersion), createObject('value', '9.10'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDependencyAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDependencyAgentConfig').enableAutomaticUpgrade), createObject('value', true()))]", + "settings": { + "value": { + "enableAMA": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAMA'), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NetworkWatcherAgent" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.NetworkWatcher" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'NetworkWatcherAgentWindows'), createObject('value', 'NetworkWatcherAgentLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNetworkWatcherAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNetworkWatcherAgentConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "DesiredStateConfiguration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Powershell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionDSCConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDSCConfig').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDSCConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDSCConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionDSCConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDSCConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "settings": "[if(contains(parameters('extensionDSCConfig'), 'settings'), createObject('value', parameters('extensionDSCConfig').settings), createObject('value', createObject()))]", + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": "[if(contains(parameters('extensionDSCConfig'), 'protectedSettings'), createObject('value', parameters('extensionDSCConfig').protectedSettings), 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "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": "[if(contains(parameters('extensionCustomScriptConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionCustomScriptConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.10'), createObject('value', '2.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionCustomScriptConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionCustomScriptConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionCustomScriptConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionCustomScriptConfig').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "AzureDiskEncryption" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureDiskEncryption'), createObject('value', 'AzureDiskEncryptionForLinux'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '2.2'), createObject('value', '1.1')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').enableAutomaticUpgrade), createObject('value', false()))]", + "forceUpdateTag": "[if(contains(parameters('extensionAzureDiskEncryptionConfig'), 'forceUpdateTag'), createObject('value', parameters('extensionAzureDiskEncryptionConfig').forceUpdateTag), createObject('value', '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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "NvidiaGpuDriverWindows" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.HpcCompute" + }, + "type": { + "value": "NvidiaGpuDriverWindows" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'typeHandlerVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').typeHandlerVersion), createObject('value', '1.4'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionNvidiaGpuDriverWindows'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionNvidiaGpuDriverWindows').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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": "HostPoolRegistration" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.PowerShell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'typeHandlerVersion'), createObject('value', parameters('extensionHostPoolRegistration').typeHandlerVersion), createObject('value', '2.77'))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionHostPoolRegistration'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionHostPoolRegistration').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionHostPoolRegistration').enableAutomaticUpgrade), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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(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": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'typeHandlerVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').typeHandlerVersion), if(equals(parameters('osType'), 'Windows'), createObject('value', '1.0'), createObject('value', '1.0')))]", + "autoUpgradeMinorVersion": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionGuestConfigurationExtension').autoUpgradeMinorVersion), createObject('value', true()))]", + "enableAutomaticUpgrade": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionGuestConfigurationExtension').enableAutomaticUpgrade), createObject('value', true()))]", + "forceUpdateTag": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'forceUpdateTag'), createObject('value', parameters('extensionGuestConfigurationExtension').forceUpdateTag), createObject('value', '1.0'))]", + "settings": "[if(contains(parameters('extensionGuestConfigurationExtension'), 'settings'), createObject('value', parameters('extensionGuestConfigurationExtension').settings), createObject('value', 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.28.1.47646", + "templateHash": "15287507275706455668" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension.", + "owner": "Azure/module-maintainers" + }, + "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')]" + }, + "dependsOn": [ + "virtualMachine" + ] + } + }, + "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('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.28.1.47646", + "templateHash": "4311417160342913977" + }, + "name": "Recovery Service Vaults Protection Container Protected Item", + "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", + "owner": "Azure/module-maintainers" + }, + "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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('vm', '2023-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork::defaultSubnet" + ] + }, + "keyVault": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-key-vault', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('keyVaultConfiguration'), 'name'), format('kv-{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "enableRbacAuthorization": { + "value": true + }, + "enableVaultForDeployment": { + "value": false + }, + "enableVaultForDiskEncryption": { + "value": false + }, + "enableVaultForTemplateDeployment": { + "value": false + }, + "networkAcls": { + "value": { + "defaultAction": "Deny", + "bypass": "AzureServices" + } + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "enablePurgeProtection": { + "value": "[coalesce(tryGet(parameters('keyVaultConfiguration'), 'enablePurgeProtection'), true())]" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Key Vault Administrator", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Key Vault Administrator", + "principalType": "ServicePrincipal" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name'))))]", + "logCategoriesAndGroups": [ + { + "category": "AuditEvent", + "enabled": true + } + ] + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "6639574961348492362" + }, + "name": "Key Vaults", + "description": "This module deploys a Key Vault.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "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 + }, + "accessPoliciesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + } + }, + "objectId": { + "type": "string", + "metadata": { + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." + } + }, + "applicationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Application ID of the client making request on behalf of a principal." + } + }, + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to keys." + } + }, + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to secrets." + } + }, + "certificates": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to certificates." + } + }, + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to storage accounts." + } + } + }, + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." + } + } + } + }, + "nullable": true + }, + "secretsType": { + "type": "array", + "items": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + } + }, + "nullable": true + }, + "keysType": { + "type": "array", + "items": { + "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/rotationPoliciesType", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + } + }, + "nullable": true + }, + "rotationPoliciesType": { + "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." + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/accessPoliciesType", + "metadata": { + "description": "Optional. All access policies to create." + } + }, + "secrets": { + "$ref": "#/definitions/secretsType", + "nullable": true, + "metadata": { + "description": "Optional. All secrets to create." + } + }, + "keys": { + "$ref": "#/definitions/keysType", + "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", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "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": "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)]" + } + } + ], + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.keyvault-vault.{0}.{1}', replace('0.6.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" + } + } + } + } + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "9363032158799379271" + }, + "name": "Key Vault Access Policies", + "description": "This module deploys a Key Vault Access Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "accessPoliciesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tenant ID that is used for authenticating requests to the key vault." + } + }, + "objectId": { + "type": "string", + "metadata": { + "description": "Required. The object ID of a user, service principal or security group in the tenant for the vault." + } + }, + "applicationId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Application ID of the client making request on behalf of a principal." + } + }, + "permissions": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "getrotationpolicy", + "import", + "list", + "purge", + "recover", + "release", + "restore", + "rotate", + "setrotationpolicy", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to keys." + } + }, + "secrets": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to secrets." + } + }, + "certificates": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "purge", + "recover", + "restore", + "setissuers", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to certificates." + } + }, + "storage": { + "type": "array", + "allowedValues": [ + "all", + "backup", + "delete", + "deletesas", + "get", + "getsas", + "list", + "listsas", + "purge", + "recover", + "regeneratekey", + "restore", + "set", + "setsas", + "update" + ], + "nullable": true, + "metadata": { + "description": "Optional. Permissions to storage accounts." + } + } + }, + "metadata": { + "description": "Required. Permissions the identity has for keys, secrets and certificates." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent key vault. Required if the template is used in a standalone deployment." + } + }, + "accessPolicies": { + "$ref": "#/definitions/accessPoliciesType", + "metadata": { + "description": "Optional. An array of 0 to 16 identities that have access to the key vault. All identities in the array must use the same tenant ID as the key vault's tenant ID." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(coalesce(parameters('accessPolicies'), createArray()))]", + "input": { + "applicationId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", + "objectId": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].objectId]", + "permissions": "[coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[coalesce(tryGet(coalesce(parameters('accessPolicies'), createArray())[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" + } + } + ] + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "policies": { + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "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(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesEnabled')]" + }, + "attributesExp": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesExp')]" + }, + "attributesNbf": { + "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesNbf')]" + }, + "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.28.1.47646", + "templateHash": "8081761451262814691" + }, + "name": "Key Vault Secrets", + "description": "This module deploys a Key Vault Secret.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[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')]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "secret_roleAssignments": { + "copy": { + "name": "secret_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/secrets/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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'))]" + }, + "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(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesEnabled')]" + }, + "attributesExp": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesExp')]" + }, + "attributesNbf": { + "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesNbf')]" + }, + "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.28.1.47646", + "templateHash": "6463446681908714455" + }, + "name": "Key Vault Keys", + "description": "This module deploys a Key Vault Key.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "keyVaultName": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "rotationPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Key rotation policy properties object." + } + } + }, + "variables": { + "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 (Preview)": "[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": { + "attributes": { + "enabled": "[parameters('attributesEnabled')]", + "exp": "[parameters('attributesExp')]", + "nbf": "[parameters('attributesNbf')]" + }, + "curveName": "[parameters('curveName')]", + "keyOps": "[parameters('keyOps')]", + "keySize": "[parameters('keySize')]", + "kty": "[parameters('kty')]", + "rotationPolicy": "[coalesce(parameters('rotationPolicy'), createObject())]", + "release_policy": "[coalesce(parameters('releasePolicy'), createObject())]" + }, + "dependsOn": [ + "keyVault" + ] + }, + "key_roleAssignments": { + "copy": { + "name": "key_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.KeyVault/vaults/{0}/keys/{1}', parameters('keyVaultName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "key" + ] + } + }, + "outputs": { + "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())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.KeyVault/vaults', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.KeyVault/vaults', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'vault')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], 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]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace", + "managedIdentityHub", + "managedIdentityProject" + ] + }, + "storageAccount": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-storage', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('storageAccountConfiguration'), 'name'), format('st{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "skuName": { + "value": "[coalesce(tryGet(parameters('storageAccountConfiguration'), 'sku'), 'Standard_RAGZRS')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "allowBlobPublicAccess": { + "value": false + }, + "allowSharedKeyAccess": { + "value": "[coalesce(tryGet(parameters('storageAccountConfiguration'), 'allowSharedKeyAccess'), true())]" + }, + "defaultToOAuthAuthentication": { + "value": "[not(coalesce(tryGet(parameters('storageAccountConfiguration'), 'allowSharedKeyAccess'), true()))]" + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "networkAcls": { + "value": { + "defaultAction": "Deny", + "bypass": "AzureServices" + } + }, + "privateEndpoints": "[if(not(equals(variables('subnetResourceId'), null())), createObject('value', map(items(variables('storagePrivateDnsZones')), lambda('zone', createObject('name', format('pep-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'service', lambdaVariables('zone').value, 'subnetResourceId', coalesce(variables('subnetResourceId'), ''), 'privateDnsZoneResourceIds', createArray(resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone').key)))))), createObject('value', null()))]", + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Reader", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Storage Account Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Storage Table Data Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Storage Blob Data Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Storage Blob Data Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "69566ab7-960f-475b-8e7c-b3118f30c6bd", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "69566ab7-960f-475b-8e7c-b3118f30c6bd", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7296091181254270609" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "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." + } + } + } + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint ip address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private ip addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private ip address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. If used must also be specified in `managedIdentities.userAssignedResourceIds`. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + } + }, + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "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" + ], + "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", + "defaultValue": {}, + "metadata": { + "description": "Optional. Provides the identity based authentication settings for Azure Files." + } + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + } + }, + "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": { + "$ref": "#/definitions/privateEndpointType", + "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", + "defaultValue": "", + "allowedValues": [ + "", + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_0", + "TLS1_1", + "TLS1_2" + ], + "metadata": { + "description": "Optional. Set the minimum TLS version on request to storage." + } + }, + "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", + "defaultValue": [], + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "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." + } + }, + "allowedCopyScope": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + } + }, + "variables": { + "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 (Preview)": "[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 SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", + "customDomain": { + "name": "[parameters('customDomainName')]", + "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + }, + "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", + "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", + "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", + "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", + "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "isHnsEnabled": "[if(parameters('enableHierarchicalNamespace'), parameters('enableHierarchicalNamespace'), null())]", + "isSftpEnabled": "[parameters('enableSftp')]", + "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", + "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], 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": "[coalesce(parameters('managementPolicyRules'), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "9473195527943694039" + }, + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy.", + "owner": "Azure/module-maintainers" + }, + "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": { + "description": "Required. The Storage Account ManagementPolicies Rules." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2023-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(parameters('localUsers'))]" + }, + "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": "[parameters('localUsers')[copyIndex()].name]" + }, + "hasSshKey": { + "value": "[parameters('localUsers')[copyIndex()].hasSshKey]" + }, + "hasSshPassword": { + "value": "[parameters('localUsers')[copyIndex()].hasSshPassword]" + }, + "permissionScopes": { + "value": "[parameters('localUsers')[copyIndex()].permissionScopes]" + }, + "hasSharedKey": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'hasSharedKey')]" + }, + "homeDirectory": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'homeDirectory')]" + }, + "sshAuthorizedKeys": { + "value": "[tryGet(parameters('localUsers')[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.28.1.47646", + "templateHash": "14968464858285923305" + }, + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "sshAuthorizedKeysType": { + "type": "secureObject", + "properties": { + "secureList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "string", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + } + }, + "metadata": { + "description": "Optional. The list of SSH authorized keys." + } + } + }, + "nullable": 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", + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "$ref": "#/definitions/sshAuthorizedKeysType", + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2023-04-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": "[tryGet(parameters('sshAuthorizedKeys'), 'secureList')]" + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "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.28.1.47646", + "templateHash": "2306287879023715578" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": 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." + } + }, + "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", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + } + }, + "defaultServiceVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." + } + }, + "deleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for blob soft delete." + } + }, + "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": 6, + "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": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "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": { + "corsRules": "[parameters('corsRules')]" + }, + "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "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')]" + }, + "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.28.1.47646", + "templateHash": "7045309160947869799" + }, + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "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 container to deploy." + } + }, + "defaultEncryptionScope": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Default the container to use specified encryption scope for all writes." + } + }, + "denyEncryptionScopeOverride": { + "type": "bool", + "defaultValue": false, + "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", + "defaultValue": {}, + "metadata": { + "description": "Optional. A name-value pair to associate with the container as metadata." + } + }, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], + "metadata": { + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", + "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + }, + "dependsOn": [ + "storageAccount::blobServices" + ] + }, + "container_roleAssignments": { + "copy": { + "name": "container_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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.28.1.47646", + "templateHash": "2543276032744560941" + }, + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "containerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." + } + }, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, + "metadata": { + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." + } + }, + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." + } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", + "properties": { + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed immutability policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed immutability policy." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed immutability policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "container", + "storageAccount" + ] + } + }, + "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'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed container." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "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')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7463227074634701879" + }, + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": 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", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Protocol settings for file service." + } + }, + "shareDeleteRetentionPolicy": { + "type": "object", + "defaultValue": { + "enabled": true, + "days": 7 + }, + "metadata": { + "description": "Optional. The service properties for soft delete." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "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', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" + }, + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "1342480740201032357" + }, + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + }, + "dependsOn": [ + "storageAccount::fileService" + ] + }, + "fileShare_roleAssignments": { + "condition": "[not(empty(parameters('roleAssignments')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Share-Rbac', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "fileShareResourceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8779226603522513073" + } + }, + "parameters": { + "roleAssignments": { + "type": "array", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "fileShareResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the file share to assign the roles to." + } + } + }, + "variables": { + "$fxv#0": { + "$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())]" + } + } + ] + }, + "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 (Preview)": "[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": [ + { + "copy": { + "name": "fileShare_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "Outer" + }, + "template": "[variables('$fxv#0')]", + "parameters": { + "scope": { + "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" + }, + "name": { + "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + }, + "roleDefinitionId": { + "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + }, + "principalId": { + "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + }, + "condition": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": { + "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + } + } + } + } + ] + } + }, + "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')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "10678250016540336570" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": 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." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "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.28.1.47646", + "templateHash": "13487964166280180730" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Required. A name-value pair that represents queue metadata." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::queueServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + }, + "dependsOn": [ + "storageAccount::queueServices" + ] + }, + "queue_roleAssignments": { + "copy": { + "name": "queue_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), 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": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "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')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "16839054392438941735" + }, + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": 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." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. tables to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "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.28.1.47646", + "templateHash": "3177845984945141330" + }, + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "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": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "dependsOn": [ + "storageAccount::tableServices" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), 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": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), 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]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "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" + ] + } + }, + "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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2022-09-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]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork::defaultSubnet", + "managedIdentityHub", + "managedIdentityProject", + "storageAccount_privateDnsZones" + ] + }, + "containerRegistry": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-container-registry', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('containerRegistryConfiguration'), 'name'), format('cr{0}', parameters('name')))]" + }, + "acrSku": { + "value": "Premium" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "networkRuleBypassOptions": { + "value": "AzureServices" + }, + "zoneRedundancy": { + "value": "Enabled" + }, + "trustPolicyStatus": { + "value": "[coalesce(tryGet(parameters('containerRegistryConfiguration'), 'trustPolicyStatus'), 'enabled')]" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "AcrPull", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "AcrPull", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "4907495916128135857" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of your Azure container registry." + } + }, + "exportPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the export policy is enabled or not." + } + }, + "quarantinePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "trustPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the retention policy is enabled or not." + } + }, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, + "metadata": { + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." + } + }, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled." + } + }, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. Soft Delete policy status. Default is disabled." + } + }, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "allowedValues": [ + "AzureServices", + "None" + ], + "metadata": { + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." + } + }, + "networkRuleSetDefaultAction": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. The default action of allow or deny when no other rules match." + } + }, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + }, + "replications": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All replications to create." + } + }, + "webhooks": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All webhooks to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "cacheRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview))." + } + } + }, + "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": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.containerregistry-registry.{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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, + "properties": { + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } + }, + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "registry_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { + "copy": { + "name": "registry_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "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": [ + "registry" + ] + }, + "registry_roleAssignments": { + "copy": { + "name": "registry_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "13769087012819677314" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "registryName": { + "value": "[parameters('name')]" + }, + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name'), replace(replace(coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository, '/', '-'), '.', '-'))]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "9456138761658214484" + }, + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", + "metadata": { + "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "cacheRule": { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Cache Rule." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Cache Rule." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Cache Rule." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_webhooks": { + "copy": { + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" + }, + "action": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action'), createArray('chart_delete', 'chart_push', 'delete', 'push', 'quarantine'))]" + }, + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" + }, + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" + }, + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" + }, + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "708950816969162319" + }, + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the webhook." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { + "type": "string", + "metadata": { + "description": "The status of the webhook." + }, + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "managedIdentityHub", + "managedIdentityProject" + ] + }, + "applicationInsights": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-appi', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('applicationInsightsConfiguration'), 'name'), format('appi-{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "kind": { + "value": "web" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "workspaceResourceId": { + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name'))))]" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "15992072010754675807" + }, + "name": "Application Insights", + "description": "This component deploys an Application Insights instance.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + } + }, + "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", + "defaultValue": "", + "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." + } + }, + "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." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.insights-component.{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" + } + } + } + } + }, + "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')]" + } + }, + "appInsights_roleAssignments": { + "copy": { + "name": "appInsights_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Insights/components', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "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": "[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.28.1.47646", + "templateHash": "11213930862679606021" + }, + "name": "Application Insights Linked Storage Account", + "description": "This component deploys an Application Insights Linked Storage Account.", + "owner": "Azure/module-maintainers" + }, + "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", + "managedIdentityHub", + "managedIdentityProject" + ] + }, + "workspaceHub": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-hub', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('workspaceConfiguration'), 'name'), format('hub-{0}', parameters('name')))]" + }, + "sku": { + "value": "Standard" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "kind": { + "value": "Hub" + }, + "associatedApplicationInsightsResourceId": { + "value": "[reference('applicationInsights').outputs.resourceId.value]" + }, + "associatedKeyVaultResourceId": { + "value": "[reference('keyVault').outputs.resourceId.value]" + }, + "associatedStorageAccountResourceId": { + "value": "[reference('storageAccount').outputs.resourceId.value]" + }, + "associatedContainerRegistryResourceId": { + "value": "[reference('containerRegistry').outputs.resourceId.value]" + }, + "workspaceHubConfig": { + "value": { + "defaultWorkspaceResourceGroup": "[resourceGroup().id]" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" + ] + } + }, + "primaryUserAssignedIdentity": { + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" + }, + "computes": { + "value": "[tryGet(parameters('workspaceConfiguration'), 'computes')]" + }, + "managedNetworkSettings": { + "value": { + "isolationMode": "[coalesce(tryGet(parameters('workspaceConfiguration'), 'networkIsolationMode'), 'AllowInternetOutbound')]", + "outboundRules": "[tryGet(parameters('workspaceConfiguration'), 'networkOutboundRules')]" + } + }, + "privateEndpoints": "[if(not(equals(variables('subnetResourceId'), null())), createObject('value', createArray(createObject('name', format('pep-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'service', variables('mlTargetSubResource'), 'subnetResourceId', coalesce(variables('subnetResourceId'), ''), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', map(objectKeys(variables('mlPrivateDnsZones')), lambda('zone', createObject('name', replace(lambdaVariables('zone'), '.', '-'), 'privateDnsZoneResourceId', resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone'))))))))), createObject('value', null()))]", + "systemDatastoresAuthMode": { + "value": "identity" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1726366914846644834" + }, + "name": "Machine Learning Services Workspaces", + "description": "This module deploys a Machine Learning Services Workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource. Must be false if `primaryUserAssignedIdentity` is provided." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "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." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "featureStoreSettingType": { + "type": "object", + "properties": { + "computeRuntime": { + "type": "object", + "properties": { + "sparkRuntimeVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The spark runtime version." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Compute runtime config for feature store type workspace." + } + }, + "offlineStoreConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The offline store connection name." + } + }, + "onlineStoreConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The online store connection name." + } + } + }, + "nullable": true + }, + "OutboundRuleType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "FQDN": { + "$ref": "#/definitions/FqdnOutboundRuleType" + }, + "PrivateEndpoint": { + "$ref": "#/definitions/PrivateEndpointOutboundRule" + }, + "ServiceTag": { + "$ref": "#/definitions/ServiceTagOutboundRule" + } + } + } + }, + "FqdnOutboundRuleType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "FQDN" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "string", + "metadata": { + "description": "Required. Fully Qualified Domain Name to allow for outbound traffic." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "PrivateEndpointOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "PrivateEndpoint" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound' or 'AllowInternetOutbound'." + } + }, + "destination": { + "type": "object", + "properties": { + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target resource for the private endpoint." + } + }, + "sparkEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the private endpoint can be used by jobs running on Spark." + } + }, + "subresourceTarget": { + "type": "string", + "metadata": { + "description": "Required. The sub resource to connect for the private endpoint." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "ServiceTagOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "ServiceTag" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "object", + "properties": { + "portRanges": { + "type": "string", + "metadata": { + "description": "Required. The name of the service tag to allow." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. The protocol to allow. Provide an asterisk(*) to allow any protocol." + } + }, + "serviceTag": { + "type": "string", + "metadata": { + "description": "Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "managedNetworkSettingType": { + "type": "object", + "properties": { + "isolationMode": { + "type": "string", + "allowedValues": [ + "AllowInternetOutbound", + "AllowOnlyApprovedOutbound", + "Disabled" + ], + "metadata": { + "description": "Required. Isolation mode for the managed network of a machine learning workspace." + } + }, + "outboundRules": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/OutboundRuleType", + "metadata": { + "description": "Required. The outbound rule. The name of the rule is the object key." + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules for the managed network of a machine learning workspace." + } + } + }, + "nullable": true + }, + "serverlessComputeSettingType": { + "type": "object", + "properties": { + "serverlessComputeCustomSubnet": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of an existing virtual network subnet in which serverless compute nodes should be deployed." + } + }, + "serverlessComputeNoPublicIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag to signal if serverless compute nodes deployed in custom vNet would have no public IP addresses for a workspace with private endpoint." + } + } + }, + "nullable": true + }, + "workspaceHubConfigType": { + "type": "object", + "properties": { + "additionalWorkspaceStorageAccounts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of additional storage accounts to attach to the workspace." + } + }, + "defaultWorkspaceResourceGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the default resource group for projects created in the workspace hub." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the machine learning workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Free", + "Basic", + "Standard", + "Premium" + ], + "metadata": { + "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." + } + }, + "kind": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "Project", + "Hub", + "FeatureStore" + ], + "metadata": { + "description": "Optional. The type of Azure Machine Learning workspace to create." + } + }, + "associatedStorageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedKeyVaultResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedApplicationInsightsResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." + } + }, + "associatedContainerRegistryResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the associated Container Registry." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "hbiWorkspace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." + } + }, + "hubResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "computes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Computes to create respectively attach to the workspace." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "defaultValue": { + "systemAssigned": true + }, + "metadata": { + "description": "Optional. The managed identity definition for this resource. At least one identity type is required." + } + }, + "featureStoreSettings": { + "$ref": "#/definitions/featureStoreSettingType", + "metadata": { + "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." + } + }, + "managedNetworkSettings": { + "$ref": "#/definitions/managedNetworkSettingType", + "metadata": { + "description": "Optional. Managed Network settings for a machine learning workspace." + } + }, + "serverlessComputeSettings": { + "$ref": "#/definitions/serverlessComputeSettingType", + "metadata": { + "description": "Optional. Settings for serverless compute created in the workspace." + } + }, + "systemDatastoresAuthMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "accessKey", + "identity" + ], + "metadata": { + "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." + } + }, + "workspaceHubConfig": { + "$ref": "#/definitions/workspaceHubConfigType", + "metadata": { + "description": "Optional. Configuration for workspace hub settings." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of this workspace." + } + }, + "discoveryUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "imageBuildCompute": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The compute name for image build." + } + }, + "primaryUserAssignedIdentity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled." + } + }, + "serviceManagedResourcesSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The service managed resource settings." + } + }, + "sharedPrivateLinkResources": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of shared private link resources in this workspace. Note: This property is not idempotent." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "AzureML Compute Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e503ece1-11d0-4e8e-8e2c-7a6c3bf38815')]", + "AzureML Data Scientist": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f6c7c914-8db3-469d-8ca1-694a8f32e121')]", + "AzureML Metrics Writer (preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '635dd51f-9968-44d3-b7fb-6d9a6bd613ae')]", + "AzureML Registry User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1823dd4f-9b8c-4ab6-ab4e-7397a3684615')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.machinelearningservices-workspace.{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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "workspace": { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2024-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]", + "tier": "[parameters('sku')]" + }, + "identity": "[variables('identity')]", + "properties": "[union(createObject('friendlyName', parameters('name'), 'storageAccount', parameters('associatedStorageAccountResourceId'), 'keyVault', parameters('associatedKeyVaultResourceId'), 'applicationInsights', parameters('associatedApplicationInsightsResourceId'), 'containerRegistry', parameters('associatedContainerRegistryResourceId'), 'hbiWorkspace', parameters('hbiWorkspace'), 'description', parameters('description'), 'discoveryUrl', parameters('discoveryUrl'), 'encryption', if(not(empty(parameters('customerManagedKey'))), createObject('status', 'Enabled', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyVaultArmId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null()), 'imageBuildCompute', parameters('imageBuildCompute'), 'primaryUserAssignedIdentity', parameters('primaryUserAssignedIdentity'), 'systemDatastoresAuthMode', parameters('systemDatastoresAuthMode'), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'serviceManagedResourcesSettings', parameters('serviceManagedResourcesSettings'), 'featureStoreSettings', parameters('featureStoreSettings'), 'hubResourceId', parameters('hubResourceId'), 'managedNetwork', parameters('managedNetworkSettings'), 'serverlessComputeSettings', parameters('serverlessComputeSettings'), 'workspaceHubConfig', parameters('workspaceHubConfig')), if(not(empty(parameters('sharedPrivateLinkResources'))), createObject('sharedPrivateLinkResources', parameters('sharedPrivateLinkResources')), createObject()))]", + "kind": "[parameters('kind')]", + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "workspace_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_diagnosticSettings": { + "copy": { + "name": "workspace_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_roleAssignments": { + "copy": { + "name": "workspace_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_computes": { + "copy": { + "name": "workspace_computes", + "count": "[length(coalesce(parameters('computes'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-compute', parameters('name'), coalesce(parameters('computes'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "machineLearningWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].name]" + }, + "location": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].location]" + }, + "sku": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'sku')]" + }, + "managedIdentities": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'managedIdentities')]" + }, + "tags": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'tags')]" + }, + "deployCompute": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'deployCompute')]" + }, + "computeLocation": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'computeLocation')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'description')]" + }, + "disableLocalAuth": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'disableLocalAuth')]" + }, + "resourceId": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'resourceId')]" + }, + "computeType": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].computeType]" + }, + "properties": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'properties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8580750401363518569" + }, + "name": "Machine Learning Services Workspaces Computes", + "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "machineLearningWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 16, + "metadata": { + "description": "Required. Name of the compute." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Specifies the location of the resource." + } + }, + "sku": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Basic", + "Free", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "deployCompute": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." + } + }, + "computeLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the Machine Learning compute." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. ARM resource ID of the underlying compute." + } + }, + "computeType": { + "type": "string", + "allowedValues": [ + "AKS", + "AmlCompute", + "ComputeInstance", + "Databricks", + "DataFactory", + "DataLakeAnalytics", + "HDInsight", + "Kubernetes", + "SynapseSpark", + "VirtualMachine" + ], + "metadata": { + "description": "Required. Set the object type." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "machineLearningWorkspace": { + "existing": true, + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('machineLearningWorkspaceName')]" + }, + "compute": { + "condition": "[equals(parameters('deployCompute'), true())]", + "type": "Microsoft.MachineLearningServices/workspaces/computes", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", + "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", + "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", + "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]", + "dependsOn": [ + "machineLearningWorkspace" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the compute." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the compute." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the compute was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('compute', '2022-10-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "workspace", + "workspace_privateEndpoints" + ] + }, + "workspace_privateEndpoints": { + "copy": { + "name": "workspace_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3308873178893851812" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "workspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the machine learning service." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the machine learning service was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the machine learning service." + }, + "value": "[parameters('name')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('workspace', '2024-04-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('workspace', '2024-04-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "applicationInsights", + "containerRegistry", + "virtualNetwork::defaultSubnet", + "keyVault", + "managedIdentityHub", + "storageAccount", + "workspaceHub_privateDnsZones" + ] + }, + "workspaceProject": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-project', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('workspaceConfiguration'), 'projectName'), format('project-{0}', parameters('name')))]" + }, + "sku": { + "value": "Standard" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "kind": { + "value": "Project" + }, + "hubResourceId": { + "value": "[reference('workspaceHub').outputs.resourceId.value]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" + ] + } + }, + "primaryUserAssignedIdentity": { + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedIdentityHub').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('managedIdentityProject').principalId]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1726366914846644834" + }, + "name": "Machine Learning Services Workspaces", + "description": "This module deploys a Machine Learning Services Workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource. Must be false if `primaryUserAssignedIdentity` is provided." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + } + }, + "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 + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "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." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "featureStoreSettingType": { + "type": "object", + "properties": { + "computeRuntime": { + "type": "object", + "properties": { + "sparkRuntimeVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The spark runtime version." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Compute runtime config for feature store type workspace." + } + }, + "offlineStoreConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The offline store connection name." + } + }, + "onlineStoreConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The online store connection name." + } + } + }, + "nullable": true + }, + "OutboundRuleType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "FQDN": { + "$ref": "#/definitions/FqdnOutboundRuleType" + }, + "PrivateEndpoint": { + "$ref": "#/definitions/PrivateEndpointOutboundRule" + }, + "ServiceTag": { + "$ref": "#/definitions/ServiceTagOutboundRule" + } + } + } + }, + "FqdnOutboundRuleType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "FQDN" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "string", + "metadata": { + "description": "Required. Fully Qualified Domain Name to allow for outbound traffic." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "PrivateEndpointOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "PrivateEndpoint" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound' or 'AllowInternetOutbound'." + } + }, + "destination": { + "type": "object", + "properties": { + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target resource for the private endpoint." + } + }, + "sparkEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the private endpoint can be used by jobs running on Spark." + } + }, + "subresourceTarget": { + "type": "string", + "metadata": { + "description": "Required. The sub resource to connect for the private endpoint." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "ServiceTagOutboundRule": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "ServiceTag" + ], + "metadata": { + "description": "Required. Type of a managed network Outbound Rule of a machine learning workspace. Only supported when 'isolationMode' is 'AllowOnlyApprovedOutbound'." + } + }, + "destination": { + "type": "object", + "properties": { + "portRanges": { + "type": "string", + "metadata": { + "description": "Required. The name of the service tag to allow." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. The protocol to allow. Provide an asterisk(*) to allow any protocol." + } + }, + "serviceTag": { + "type": "string", + "metadata": { + "description": "Required. Which ports will be allow traffic by this rule. Provide an asterisk(*) to allow any port." + } + } + }, + "metadata": { + "description": "Required. Service Tag destination for a Service Tag Outbound Rule for the managed network of a machine learning workspace." + } + }, + "category": { + "type": "string", + "allowedValues": [ + "Dependency", + "Recommended", + "Required", + "UserDefined" + ], + "nullable": true, + "metadata": { + "description": "Optional. Category of a managed network Outbound Rule of a machine learning workspace." + } + } + } + }, + "managedNetworkSettingType": { + "type": "object", + "properties": { + "isolationMode": { + "type": "string", + "allowedValues": [ + "AllowInternetOutbound", + "AllowOnlyApprovedOutbound", + "Disabled" + ], + "metadata": { + "description": "Required. Isolation mode for the managed network of a machine learning workspace." + } + }, + "outboundRules": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/OutboundRuleType", + "metadata": { + "description": "Required. The outbound rule. The name of the rule is the object key." + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Outbound rules for the managed network of a machine learning workspace." + } + } + }, + "nullable": true + }, + "serverlessComputeSettingType": { + "type": "object", + "properties": { + "serverlessComputeCustomSubnet": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of an existing virtual network subnet in which serverless compute nodes should be deployed." + } + }, + "serverlessComputeNoPublicIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag to signal if serverless compute nodes deployed in custom vNet would have no public IP addresses for a workspace with private endpoint." + } + } + }, + "nullable": true + }, + "workspaceHubConfigType": { + "type": "object", + "properties": { + "additionalWorkspaceStorageAccounts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of additional storage accounts to attach to the workspace." + } + }, + "defaultWorkspaceResourceGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the default resource group for projects created in the workspace hub." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the machine learning workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Free", + "Basic", + "Standard", + "Premium" + ], + "metadata": { + "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." + } + }, + "kind": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "Project", + "Hub", + "FeatureStore" + ], + "metadata": { + "description": "Optional. The type of Azure Machine Learning workspace to create." + } + }, + "associatedStorageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedKeyVaultResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedApplicationInsightsResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." + } + }, + "associatedContainerRegistryResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the associated Container Registry." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "hbiWorkspace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." + } + }, + "hubResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "computes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Computes to create respectively attach to the workspace." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "defaultValue": { + "systemAssigned": true + }, + "metadata": { + "description": "Optional. The managed identity definition for this resource. At least one identity type is required." + } + }, + "featureStoreSettings": { + "$ref": "#/definitions/featureStoreSettingType", + "metadata": { + "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." + } + }, + "managedNetworkSettings": { + "$ref": "#/definitions/managedNetworkSettingType", + "metadata": { + "description": "Optional. Managed Network settings for a machine learning workspace." + } + }, + "serverlessComputeSettings": { + "$ref": "#/definitions/serverlessComputeSettingType", + "metadata": { + "description": "Optional. Settings for serverless compute created in the workspace." + } + }, + "systemDatastoresAuthMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "accessKey", + "identity" + ], + "metadata": { + "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." + } + }, + "workspaceHubConfig": { + "$ref": "#/definitions/workspaceHubConfigType", + "metadata": { + "description": "Optional. Configuration for workspace hub settings." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of this workspace." + } + }, + "discoveryUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "imageBuildCompute": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The compute name for image build." + } + }, + "primaryUserAssignedIdentity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The user assigned identity resource ID that represents the workspace identity. Required if 'userAssignedIdentities' is not empty and may not be used if 'systemAssignedIdentity' is enabled." + } + }, + "serviceManagedResourcesSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The service managed resource settings." + } + }, + "sharedPrivateLinkResources": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of shared private link resources in this workspace. Note: This property is not idempotent." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "AzureML Compute Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e503ece1-11d0-4e8e-8e2c-7a6c3bf38815')]", + "AzureML Data Scientist": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f6c7c914-8db3-469d-8ca1-694a8f32e121')]", + "AzureML Metrics Writer (preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '635dd51f-9968-44d3-b7fb-6d9a6bd613ae')]", + "AzureML Registry User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1823dd4f-9b8c-4ab6-ab4e-7397a3684615')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.machinelearningservices-workspace.{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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "workspace": { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2024-04-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]", + "tier": "[parameters('sku')]" + }, + "identity": "[variables('identity')]", + "properties": "[union(createObject('friendlyName', parameters('name'), 'storageAccount', parameters('associatedStorageAccountResourceId'), 'keyVault', parameters('associatedKeyVaultResourceId'), 'applicationInsights', parameters('associatedApplicationInsightsResourceId'), 'containerRegistry', parameters('associatedContainerRegistryResourceId'), 'hbiWorkspace', parameters('hbiWorkspace'), 'description', parameters('description'), 'discoveryUrl', parameters('discoveryUrl'), 'encryption', if(not(empty(parameters('customerManagedKey'))), createObject('status', 'Enabled', 'identity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), createObject('userAssignedIdentity', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null()), 'keyVaultProperties', createObject('keyVaultArmId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null()), 'imageBuildCompute', parameters('imageBuildCompute'), 'primaryUserAssignedIdentity', parameters('primaryUserAssignedIdentity'), 'systemDatastoresAuthMode', parameters('systemDatastoresAuthMode'), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'serviceManagedResourcesSettings', parameters('serviceManagedResourcesSettings'), 'featureStoreSettings', parameters('featureStoreSettings'), 'hubResourceId', parameters('hubResourceId'), 'managedNetwork', parameters('managedNetworkSettings'), 'serverlessComputeSettings', parameters('serverlessComputeSettings'), 'workspaceHubConfig', parameters('workspaceHubConfig')), if(not(empty(parameters('sharedPrivateLinkResources'))), createObject('sharedPrivateLinkResources', parameters('sharedPrivateLinkResources')), createObject()))]", + "kind": "[parameters('kind')]", + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "workspace_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_diagnosticSettings": { + "copy": { + "name": "workspace_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_roleAssignments": { + "copy": { + "name": "workspace_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "workspace_computes": { + "copy": { + "name": "workspace_computes", + "count": "[length(coalesce(parameters('computes'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-compute', parameters('name'), coalesce(parameters('computes'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "machineLearningWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].name]" + }, + "location": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].location]" + }, + "sku": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'sku')]" + }, + "managedIdentities": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'managedIdentities')]" + }, + "tags": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'tags')]" + }, + "deployCompute": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'deployCompute')]" + }, + "computeLocation": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'computeLocation')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'description')]" + }, + "disableLocalAuth": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'disableLocalAuth')]" + }, + "resourceId": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'resourceId')]" + }, + "computeType": { + "value": "[coalesce(parameters('computes'), createArray())[copyIndex()].computeType]" + }, + "properties": { + "value": "[tryGet(coalesce(parameters('computes'), createArray())[copyIndex()], 'properties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8580750401363518569" + }, + "name": "Machine Learning Services Workspaces Computes", + "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "machineLearningWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 16, + "metadata": { + "description": "Required. Name of the compute." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Specifies the location of the resource." + } + }, + "sku": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Basic", + "Free", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "deployCompute": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." + } + }, + "computeLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the Machine Learning compute." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. ARM resource ID of the underlying compute." + } + }, + "computeType": { + "type": "string", + "allowedValues": [ + "AKS", + "AmlCompute", + "ComputeInstance", + "Databricks", + "DataFactory", + "DataLakeAnalytics", + "HDInsight", + "Kubernetes", + "SynapseSpark", + "VirtualMachine" + ], + "metadata": { + "description": "Required. Set the object type." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "machineLearningWorkspace": { + "existing": true, + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('machineLearningWorkspaceName')]" + }, + "compute": { + "condition": "[equals(parameters('deployCompute'), true())]", + "type": "Microsoft.MachineLearningServices/workspaces/computes", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", + "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", + "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", + "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]", + "dependsOn": [ + "machineLearningWorkspace" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the compute." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the compute." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the compute was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('compute', '2022-10-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "workspace", + "workspace_privateEndpoints" + ] + }, + "workspace_privateEndpoints": { + "copy": { + "name": "workspace_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'amlworkspace')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3308873178893851812" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "workspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the machine learning service." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the machine learning service was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the machine learning service." + }, + "value": "[parameters('name')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('workspace', '2024-04-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('workspace', '2024-04-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "managedIdentityHub", + "managedIdentityProject", + "workspaceHub" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the module was deployed to." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the module was deployed to." + }, + "value": "[parameters('location')]" + }, + "applicationInsightsResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application insights component." + }, + "value": "[reference('applicationInsights').outputs.resourceId.value]" + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the application insights component." + }, + "value": "[reference('applicationInsights').outputs.name.value]" + }, + "applicationInsightsApplicationId": { + "type": "string", + "metadata": { + "description": "The application ID of the application insights component." + }, + "value": "[reference('applicationInsights').outputs.applicationId.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "metadata": { + "description": "The instrumentation key of the application insights component." + }, + "value": "[reference('applicationInsights').outputs.instrumentationKey.value]" + }, + "applicationInsightsConnectionString": { + "type": "string", + "metadata": { + "description": "The connection string of the application insights component." + }, + "value": "[reference('applicationInsights').outputs.connectionString.value]" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the log analytics workspace." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name'))))]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "The name of the log analytics workspace." + }, + "value": "[coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name')))]" + }, + "managedIdentityHubResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the workspace hub user assigned managed identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" + }, + "managedIdentityHubName": { + "type": "string", + "metadata": { + "description": "The name of the workspace hub user assigned managed identity." + }, + "value": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name')))]" + }, + "managedIdentityHubPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the workspace hub user assigned managed identity." + }, + "value": "[reference('managedIdentityHub').principalId]" + }, + "managedIdentityHubClientId": { + "type": "string", + "metadata": { + "description": "The client ID of the workspace hub user assigned managed identity." + }, + "value": "[reference('managedIdentityHub').clientId]" + }, + "managedIdentityProjectResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the workspace project user assigned managed identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" + }, + "managedIdentityProjectName": { + "type": "string", + "metadata": { + "description": "The name of the workspace project user assigned managed identity." + }, + "value": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name')))]" + }, + "managedIdentityProjectPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the workspace project user assigned managed identity." + }, + "value": "[reference('managedIdentityProject').principalId]" + }, + "managedIdentityProjectClientId": { + "type": "string", + "metadata": { + "description": "The client ID of the workspace project user assigned managed identity." + }, + "value": "[reference('managedIdentityProject').clientId]" + }, + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[reference('keyVault').outputs.resourceId.value]" + }, + "keyVaultName": { + "type": "string", + "metadata": { + "description": "The name of the key vault." + }, + "value": "[reference('keyVault').outputs.name.value]" + }, + "keyVaultUri": { + "type": "string", + "metadata": { + "description": "The URI of the key vault." + }, + "value": "[reference('keyVault').outputs.uri.value]" + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the storage account." + }, + "value": "[reference('storageAccount').outputs.resourceId.value]" + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "The name of the storage account." + }, + "value": "[reference('storageAccount').outputs.name.value]" + }, + "containerRegistryResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container registry." + }, + "value": "[reference('containerRegistry').outputs.resourceId.value]" + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "The name of the container registry." + }, + "value": "[reference('containerRegistry').outputs.name.value]" + }, + "workspaceHubResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the workspace hub." + }, + "value": "[reference('workspaceHub').outputs.resourceId.value]" + }, + "workspaceHubName": { + "type": "string", + "metadata": { + "description": "The name of the workspace hub." + }, + "value": "[reference('workspaceHub').outputs.name.value]" + }, + "workspaceProjectResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the workspace project." + }, + "value": "[reference('workspaceProject').outputs.resourceId.value]" + }, + "workspaceProjectName": { + "type": "string", + "metadata": { + "description": "The name of the workspace project." + }, + "value": "[reference('workspaceProject').outputs.name.value]" + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name')))), '')]" + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[if(variables('createVirtualNetwork'), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), '')]" + }, + "virtualNetworkSubnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the subnet in the virtual network." + }, + "value": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default')), '')]" + }, + "virtualNetworkSubnetName": { + "type": "string", + "metadata": { + "description": "The name of the subnet in the virtual network." + }, + "value": "[if(variables('createVirtualNetwork'), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'), '')]" + }, + "bastionResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Bastion host." + }, + "value": "[if(variables('createBastion'), reference('bastion').outputs.resourceId.value, '')]" + }, + "bastionName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Bastion host." + }, + "value": "[if(variables('createBastion'), reference('bastion').outputs.name.value, '')]" + }, + "virtualMachineResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual machine." + }, + "value": "[if(variables('createVirtualMachine'), reference('virtualMachine').outputs.resourceId.value, '')]" + }, + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "The name of the virtual machine." + }, + "value": "[if(variables('createVirtualMachine'), reference('virtualMachine').outputs.name.value, '')]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/defaults/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..6bbf94c845 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,58 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aiplatform-baseline-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aipbmin' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + virtualMachineConfiguration: { + adminUsername: 'localAdminUser' + adminPassword: password + } + } + } +] diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..d2ea57bb6a --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep @@ -0,0 +1,206 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Maintenance Configuration to create.') +param maintenanceConfigurationName string + +@description('Required. The name of the network security group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Bastion Network Security Group to create.') +param networkSecurityGroupBastionName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { + name: maintenanceConfigurationName + location: location + properties: { + extensionProperties: { + InGuestPatchMode: 'User' + } + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + startDateTime: '2024-06-16 00:00' + duration: '03:55' + timeZone: 'W. Europe Standard Time' + recurEvery: '1Day' + } + visibility: 'Custom' + installPatches: { + rebootSetting: 'IfRequired' + windowsParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'DenyAnySSHInbound' + properties: { + access: 'Deny' + direction: 'Inbound' + protocol: 'Tcp' + priority: 100 + sourcePortRange: '*' + sourceAddressPrefix: '*' + destinationPortRange: '22' + destinationAddressPrefix: '*' + } + } + ] + } +} + +resource networkSecurityGroupBastion 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupBastionName + location: location + properties: { + securityRules: [ + { + name: 'AllowHttpsInbound' + properties: { + priority: 120 + protocol: 'Tcp' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: 'Internet' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'AllowGatewayManagerInbound' + properties: { + priority: 130 + protocol: 'Tcp' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: 'GatewayManager' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'AllowAzureLoadBalancerInbound' + properties: { + priority: 140 + protocol: 'Tcp' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: 'AzureLoadBalancer' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'AllowBastionHostCommunication' + properties: { + priority: 150 + protocol: '*' + access: 'Allow' + direction: 'Inbound' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: 'VirtualNetwork' + destinationPortRanges: [ + '8080' + '5701' + ] + } + } + { + name: 'AllowSshOutbound' + properties: { + priority: 100 + protocol: '*' + access: 'Allow' + direction: 'Outbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: 'VirtualNetwork' + destinationPortRanges: [ + '22' + '3389' + ] + } + } + { + name: 'AllowAzureCloudOutbound' + properties: { + priority: 110 + protocol: 'Tcp' + access: 'Allow' + direction: 'Outbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: 'AzureCloud' + destinationPortRange: '443' + } + } + { + name: 'AllowBastionCommunication' + properties: { + priority: 120 + protocol: '*' + access: 'Allow' + direction: 'Outbound' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: 'VirtualNetwork' + destinationPortRanges: [ + '8080' + '5701' + ] + } + } + { + name: 'AllowHttpOutbound' + properties: { + priority: 130 + protocol: '*' + access: 'Allow' + direction: 'Outbound' + sourceAddressPrefix: '*' + sourcePortRange: '*' + destinationAddressPrefix: 'Internet' + destinationPortRange: '80' + } + } + ] + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Network Security Group.') +output networkSecurityGroupResourceId string = networkSecurityGroup.id + +@description('The resource ID of the created Bastion Network Security Group.') +output networkSecurityGroupBastionResourceId string = networkSecurityGroupBastion.id + +@description('The resource ID of the maintenance configuration.') +output maintenanceConfigurationResourceId string = maintenanceConfiguration.id diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..240496050a --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep @@ -0,0 +1,171 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aiplatform-baseline-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aipbmax' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + storageAccountName: 'dep${namePrefix}st${serviceShort}' + maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' + networkSecurityGroupName: 'dep${namePrefix}nsg${serviceShort}' + networkSecurityGroupBastionName: 'dep-${namePrefix}-nsg-bastion-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}' + managedIdentityConfiguration: { + hubName: '${namePrefix}-id-hub-${serviceShort}' + projectName: '${namePrefix}-id-project-${serviceShort}' + } + logAnalyticsConfiguration: { + name: '${namePrefix}-log-${serviceShort}' + } + keyVaultConfiguration: { + name: '${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + enablePurgeProtection: false + } + storageAccountConfiguration: { + name: '${namePrefix}st${serviceShort}' + sku: 'Standard_GRS' + allowSharedKeyAccess: true + } + containerRegistryConfiguration: { + name: '${namePrefix}cr${serviceShort}' + trustPolicyStatus: 'disabled' + } + applicationInsightsConfiguration: { + name: '${namePrefix}-appi-${serviceShort}' + } + virtualNetworkConfiguration: { + name: '${namePrefix}-vnet-${serviceShort}' + addressPrefix: '10.1.0.0/16' + enabled: true + subnet: { + name: '${namePrefix}-snet-${serviceShort}' + addressPrefix: '10.1.0.0/24' + networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupResourceId + } + } + bastionConfiguration: { + enabled: true + name: '${namePrefix}-bas-${serviceShort}' + sku: 'Standard' + networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupBastionResourceId + subnetAddressPrefix: '10.1.1.0/26' + disableCopyPaste: true + enableFileCopy: true + enableIpConnect: true + enableKerberos: true + enableShareableLink: true + scaleUnits: 3 + } + virtualMachineConfiguration: { + enabled: true + name: take('${namePrefix}-vm-${serviceShort}', 15) + zone: 0 + size: 'Standard_DS1_v2' + adminUsername: 'localAdminUser' + adminPassword: password + nicConfigurationConfiguration: { + name: '${namePrefix}-nic-${serviceShort}' + ipConfigName: '${namePrefix}-ipcfg-${serviceShort}' + privateIPAllocationMethod: 'Dynamic' + networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupResourceId + } + imageReference: { + publisher: 'microsoft-dsvm' + offer: 'dsvm-win-2022' + sku: 'winserver-2022' + version: 'latest' + } + osDisk: { + name: '${namePrefix}-disk-${serviceShort}' + diskSizeGB: 256 + createOption: 'FromImage' + caching: 'ReadOnly' + managedDisk: { + storageAccountType: 'Standard_LRS' + } + deleteOption: 'Delete' + } + patchMode: 'AutomaticByPlatform' + encryptionAtHost: false + enableAadLoginExtension: true + enableAzureMonitorAgent: true + maintenanceConfigurationResourceId: nestedDependencies.outputs.maintenanceConfigurationResourceId + } + workspaceConfiguration: { + name: '${namePrefix}-hub-${serviceShort}' + projectName: '${namePrefix}-project-${serviceShort}' + computes: [ + { + computeType: 'ComputeInstance' + name: 'c-${substring(uniqueString(baseTime), 0, 3)}-${serviceShort}' + description: 'Default' + location: resourceLocation + properties: { + vmSize: 'STANDARD_DS11_V2' + } + sku: 'Standard' + } + ] + networkIsolationMode: 'AllowOnlyApprovedOutbound' + networkOutboundRules: { + rule1: { + type: 'FQDN' + destination: 'pypi.org' + category: 'UserDefined' + } + } + } + } + } +] diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/no-vm/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/no-vm/main.test.bicep new file mode 100644 index 0000000000..f625b82319 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/no-vm/main.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +metadata name = 'Without virtual machine' +metadata description = 'This instance deploys the module with a virtual network, but no virtual machine or Azure Bastion host.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aiplatform-baseline-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aipbnovm' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + bastionConfiguration: { + enabled: false + } + virtualMachineConfiguration: { + enabled: false + } + } + } +] diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/no-vnet/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/no-vnet/main.test.bicep new file mode 100644 index 0000000000..2a6d709944 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/no-vnet/main.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'subscription' + +metadata name = 'Without virtual network' +metadata description = 'This instance deploys the module without a virtual network, virtual machine or Azure Bastion host.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aiplatform-baseline-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aipbnovnet' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + virtualNetworkConfiguration: { + enabled: false + } + } + } +] diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..299d71ad37 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,50 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Maintenance Configuration to create.') +param maintenanceConfigurationName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { + name: maintenanceConfigurationName + location: location + properties: { + extensionProperties: { + InGuestPatchMode: 'User' + } + maintenanceScope: 'InGuestPatch' + maintenanceWindow: { + startDateTime: '2024-06-16 00:00' + duration: '03:55' + timeZone: 'W. Europe Standard Time' + recurEvery: '1Day' + } + visibility: 'Custom' + installPatches: { + rebootSetting: 'IfRequired' + windowsParameters: { + classificationsToInclude: [ + 'Critical' + 'Security' + ] + } + } + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the maintenance configuration.') +output maintenanceConfigurationResourceId string = maintenanceConfiguration.id diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..21d1ba8934 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,91 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aiplatform-baseline-${serviceShort}-rg' + +// Enforce uksouth to avoid restrictions around VM zones in certain regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aipbwaf' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' + params: { + storageAccountName: 'dep${namePrefix}st${serviceShort}' + maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' + location: enforcedLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + virtualMachineConfiguration: { + adminUsername: 'localAdminUser' + adminPassword: password + enableAadLoginExtension: true + enableAzureMonitorAgent: true + maintenanceConfigurationResourceId: nestedDependencies.outputs.maintenanceConfigurationResourceId + patchMode: 'AutomaticByPlatform' + zone: 1 + } + workspaceConfiguration: { + networkIsolationMode: 'AllowOnlyApprovedOutbound' + networkOutboundRules: { + rule: { + type: 'PrivateEndpoint' + destination: { + serviceResourceId: nestedDependencies.outputs.storageAccountResourceId + subresourceTarget: 'blob' + } + category: 'UserDefined' + } + } + } + tags: { + Env: 'test' + 'hidden-title': 'This is visible in the resource name' + } + } + } +] diff --git a/avm/ptn/ai-platform/baseline/version.json b/avm/ptn/ai-platform/baseline/version.json new file mode 100644 index 0000000000..aa34c4d0f5 --- /dev/null +++ b/avm/ptn/ai-platform/baseline/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.4", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/ptn/authorization/policy-assignment/README.md b/avm/ptn/authorization/policy-assignment/README.md index c5fb8cccb4..5d4707af32 100644 --- a/avm/ptn/authorization/policy-assignment/README.md +++ b/avm/ptn/authorization/policy-assignment/README.md @@ -875,7 +875,7 @@ module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/azd/container-apps:`. + +- [With zoneRedundant enabled](#example-1-with-zoneredundant-enabled) + +### Example 1: _With zoneRedundant enabled_ + +This instance deploys the module with zoneRedundant enabled. + + +

+ +via Bicep module + +```bicep +module containerApps 'br/public:avm/ptn/azd/container-apps:' = { + name: 'containerAppsDeployment' + params: { + // Required parameters + containerAppsEnvironmentName: 'acazrcae001' + containerRegistryName: 'acazrcr001' + logAnalyticsWorkspaceResourceId: '' + // Non-required parameters + acrSku: 'Standard' + dockerBridgeCidr: '172.16.0.1/28' + infrastructureResourceGroupName: '' + infrastructureSubnetResourceId: '' + internal: true + location: '' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + workloadProfiles: [ + { + maximumCount: 3 + minimumCount: 0 + name: 'CAW01' + workloadProfileType: 'D4' + } + ] + zoneRedundant: true + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containerAppsEnvironmentName": { + "value": "acazrcae001" + }, + "containerRegistryName": { + "value": "acazrcr001" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "" + }, + // Non-required parameters + "acrSku": { + "value": "Standard" + }, + "dockerBridgeCidr": { + "value": "172.16.0.1/28" + }, + "infrastructureResourceGroupName": { + "value": "" + }, + "infrastructureSubnetResourceId": { + "value": "" + }, + "internal": { + "value": true + }, + "location": { + "value": "" + }, + "platformReservedCidr": { + "value": "172.17.17.0/24" + }, + "platformReservedDnsIP": { + "value": "172.17.17.17" + }, + "workloadProfiles": { + "value": [ + { + "maximumCount": 3, + "minimumCount": 0, + "name": "CAW01", + "workloadProfileType": "D4" + } + ] + }, + "zoneRedundant": { + "value": true + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`containerAppsEnvironmentName`](#parameter-containerappsenvironmentname) | string | Name of the Container Apps Managed Environment. | +| [`containerRegistryName`](#parameter-containerregistryname) | string | Name of the Azure Container Registry. | +| [`logAnalyticsWorkspaceResourceId`](#parameter-loganalyticsworkspaceresourceid) | string | Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990). | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dockerBridgeCidr`](#parameter-dockerbridgecidr) | string | 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`](#parameter-infrastructuresubnetresourceid) | string | 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`](#parameter-internal) | bool | 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`](#parameter-platformreservedcidr) | string | 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`](#parameter-platformreserveddnsip) | string | 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. | +| [`workloadProfiles`](#parameter-workloadprofiles) | array | Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`acrAdminUserEnabled`](#parameter-acradminuserenabled) | bool | Enable admin user that have push / pull permission to the registry. | +| [`acrSku`](#parameter-acrsku) | string | SKU settings. Default is "Standard". | +| [`appInsightsConnectionString`](#parameter-appinsightsconnectionstring) | securestring | Application Insights connection string. | +| [`containerRegistryResourceGroupName`](#parameter-containerregistryresourcegroupname) | string | Name of the Azure Container Registry Resource Group. | +| [`daprAIInstrumentationKey`](#parameter-dapraiinstrumentationkey) | securestring | Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`infrastructureResourceGroupName`](#parameter-infrastructureresourcegroupname) | string | 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. | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`zoneRedundant`](#parameter-zoneredundant) | bool | Zone redundancy setting. | + +### Parameter: `containerAppsEnvironmentName` + +Name of the Container Apps Managed Environment. + +- Required: Yes +- Type: string + +### Parameter: `containerRegistryName` + +Name of the Azure Container Registry. + +- Required: Yes +- Type: string + +### Parameter: `logAnalyticsWorkspaceResourceId` + +Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990). + +- Required: Yes +- Type: string + +### Parameter: `dockerBridgeCidr` + +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. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `infrastructureSubnetResourceId` + +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. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `internal` + +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. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `platformReservedCidr` + +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. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `platformReservedDnsIP` + +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. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `workloadProfiles` + +Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `acrAdminUserEnabled` + +Enable admin user that have push / pull permission to the registry. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `acrSku` + +SKU settings. Default is "Standard". + +- Required: No +- Type: string +- Default: `'Standard'` +- Allowed: + ```Bicep + [ + 'Basic' + 'Premium' + 'Standard' + ] + ``` + +### Parameter: `appInsightsConnectionString` + +Application Insights connection string. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `containerRegistryResourceGroupName` + +Name of the Azure Container Registry Resource Group. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `daprAIInstrumentationKey` + +Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `infrastructureResourceGroupName` + +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. + +- Required: No +- Type: string +- Default: `[take(format('ME_{0}', parameters('containerAppsEnvironmentName')), 63)]` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `zoneRedundant` + +Zone redundancy setting. + +- Required: No +- Type: bool +- Default: `True` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `defaultDomain` | string | The Default domain of the Managed Environment. | +| `environmentName` | string | The name of the Managed Environment. | +| `environmentResourceId` | string | The resource ID of the Managed Environment. | +| `registryLoginServer` | string | The reference to the Azure container registry. | +| `registryName` | string | The Name of the Azure container registry. | +| `resourceGroupName` | string | The name of the resource group the all resources was deployed into. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/app/managed-environment:0.7.0` | Remote reference | +| `br/public:avm/res/container-registry/registry:0.4.0` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/azd/container-apps/main.bicep b/avm/ptn/azd/container-apps/main.bicep new file mode 100644 index 0000000000..9a99522cd4 --- /dev/null +++ b/avm/ptn/azd/container-apps/main.bicep @@ -0,0 +1,138 @@ +metadata name = 'avm/ptn/azd/container-apps' +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Required. Name of the Container Apps Managed Environment.') +param containerAppsEnvironmentName string + +@description('Required. Name of the Azure Container Registry.') +param containerRegistryName string + +@description('Optional. Name of the Azure Container Registry Resource Group.') +param containerRegistryResourceGroupName string = '' + +@description('Optional. Enable admin user that have push / pull permission to the registry.') +param acrAdminUserEnabled bool = false + +@description('Required. Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990).') +param logAnalyticsWorkspaceResourceId string + +@description('Optional. Application Insights connection string.') +@secure() +param appInsightsConnectionString string = '' + +@description('Optional. Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry.') +@secure() +param daprAIInstrumentationKey string = '' + +@description('Optional. SKU settings. Default is "Standard".') +@allowed([ + 'Basic' + 'Premium' + 'Standard' +]) +param acrSku string = 'Standard' + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Zone redundancy setting.') +param zoneRedundant bool = true + +@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.') +param dockerBridgeCidr string = '' + +@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.') +param infrastructureSubnetResourceId string = '' + +@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.') +param internal bool = false + +@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.') +param platformReservedCidr string = '' + +@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.') +param platformReservedDnsIP string = '' + +@description('Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant.') +param workloadProfiles array = [] + +@description('Optional. 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.') +param infrastructureResourceGroupName string = take('ME_${containerAppsEnvironmentName}', 63) + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.azd-containerapps.${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' + } + } + } + } +} + +module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.7.0' = { + name: take('containerAppsEnvironment-${deployment().name}-deployment', 64) + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + daprAIInstrumentationKey: daprAIInstrumentationKey + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + appInsightsConnectionString: appInsightsConnectionString + zoneRedundant: zoneRedundant + workloadProfiles: workloadProfiles + infrastructureResourceGroupName: infrastructureResourceGroupName + internal: internal + infrastructureSubnetId: infrastructureSubnetResourceId + dockerBridgeCidr: dockerBridgeCidr + platformReservedCidr: platformReservedCidr + platformReservedDnsIP: platformReservedDnsIP + } +} + +module containerRegistry 'br/public:avm/res/container-registry/registry:0.4.0' = { + name: take('containerRegistry-${deployment().name}-deployment', 64) + scope: !empty(containerRegistryResourceGroupName) + ? resourceGroup(containerRegistryResourceGroupName) + : resourceGroup() + params: { + name: containerRegistryName + location: location + acrAdminUserEnabled: acrAdminUserEnabled + tags: tags + acrSku: acrSku + } +} + +@description('The name of the resource group the all resources was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The Default domain of the Managed Environment.') +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain + +@description('The name of the Managed Environment.') +output environmentName string = containerAppsEnvironment.outputs.name + +@description('The resource ID of the Managed Environment.') +output environmentResourceId string = containerAppsEnvironment.outputs.resourceId + +@description('The reference to the Azure container registry.') +output registryLoginServer string = containerRegistry.outputs.loginServer + +@description('The Name of the Azure container registry.') +output registryName string = containerRegistry.outputs.name diff --git a/avm/ptn/azd/container-apps/main.json b/avm/ptn/azd/container-apps/main.json new file mode 100644 index 0000000000..b781ce346e --- /dev/null +++ b/avm/ptn/azd/container-apps/main.json @@ -0,0 +1,3337 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "3154922260961626340" + }, + "name": "avm/ptn/azd/container-apps", + "description": "Creates an Azure Container Registry and an Azure Container Apps environment.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "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." + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container Apps Managed Environment." + } + }, + "containerRegistryName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Container Registry." + } + }, + "containerRegistryResourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the Azure Container Registry Resource Group." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990)." + } + }, + "appInsightsConnectionString": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Application Insights connection string." + } + }, + "daprAIInstrumentationKey": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. SKU settings. Default is \"Standard\"." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Zone redundancy setting." + } + }, + "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." + } + }, + "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('containerAppsEnvironmentName')), 63)]", + "metadata": { + "description": "Optional. 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." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.azd-containerapps.{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" + } + } + } + } + }, + "containerAppsEnvironment": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('containerAppsEnvironment-{0}-deployment', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerAppsEnvironmentName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "daprAIInstrumentationKey": { + "value": "[parameters('daprAIInstrumentationKey')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "appInsightsConnectionString": { + "value": "[parameters('appInsightsConnectionString')]" + }, + "zoneRedundant": { + "value": "[parameters('zoneRedundant')]" + }, + "workloadProfiles": { + "value": "[parameters('workloadProfiles')]" + }, + "infrastructureResourceGroupName": { + "value": "[parameters('infrastructureResourceGroupName')]" + }, + "internal": { + "value": "[parameters('internal')]" + }, + "infrastructureSubnetId": { + "value": "[parameters('infrastructureSubnetResourceId')]" + }, + "dockerBridgeCidr": { + "value": "[parameters('dockerBridgeCidr')]" + }, + "platformReservedCidr": { + "value": "[parameters('platformReservedCidr')]" + }, + "platformReservedDnsIP": { + "value": "[parameters('platformReservedDnsIP')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8714567000770152884" + }, + "name": "App ManagedEnvironments", + "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "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 + }, + "storageType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container Apps Managed Environment." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990)." + } + }, + "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/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "logsDestination": { + "type": "string", + "defaultValue": "log-analytics", + "metadata": { + "description": "Optional. Logs destination." + } + }, + "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." + } + }, + "infrastructureSubnetId": { + "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." + } + }, + "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", + "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": { + "$ref": "#/definitions/storageType", + "metadata": { + "description": "Optional. The list of storages to mount on the 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 (Preview)": "[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": "storage", + "count": "[length(coalesce(parameters('storages'), createArray()))]" + }, + "type": "Microsoft.App/managedEnvironments/storages", + "apiVersion": "2024-02-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-03-01", + "name": "[format('46d3xbcp.res.app-managedenvironment.{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" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2023-09-01", + "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", + "name": "[last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))]" + }, + "managedEnvironment": { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2024-02-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "appInsightsConfiguration": { + "connectionString": "[parameters('appInsightsConnectionString')]" + }, + "appLogsConfiguration": { + "destination": "[parameters('logsDestination')]", + "logAnalyticsConfiguration": { + "customerId": "[reference('logAnalyticsWorkspace').customerId]", + "sharedKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))), '2023-09-01').primarySharedKey]" + } + }, + "daprAIConnectionString": "[parameters('daprAIConnectionString')]", + "daprAIInstrumentationKey": "[parameters('daprAIInstrumentationKey')]", + "customDomainConfiguration": { + "certificatePassword": "[parameters('certificatePassword')]", + "certificateValue": "[if(not(empty(parameters('certificateValue'))), parameters('certificateValue'), null())]", + "dnsSuffix": "[parameters('dnsSuffix')]" + }, + "openTelemetryConfiguration": "[if(not(empty(parameters('openTelemetryConfiguration'))), parameters('openTelemetryConfiguration'), null())]", + "peerTrafficConfiguration": { + "encryption": { + "enabled": "[parameters('peerTrafficEncryption')]" + } + }, + "vnetConfiguration": { + "internal": "[parameters('internal')]", + "infrastructureSubnetId": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('infrastructureSubnetId'), null())]", + "dockerBridgeCidr": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('dockerBridgeCidr'), null())]", + "platformReservedCidr": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedCidr'), null())]", + "platformReservedDnsIP": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedDnsIP'), null())]" + }, + "workloadProfiles": "[if(not(empty(parameters('workloadProfiles'))), parameters('workloadProfiles'), null())]", + "zoneRedundant": "[parameters('zoneRedundant')]", + "infrastructureResourceGroup": "[parameters('infrastructureResourceGroupName')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "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" + ] + } + }, + "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-02-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", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2024-02-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]" + } + } + } + } + }, + "containerRegistry": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('containerRegistry-{0}-deployment', deployment().name), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('containerRegistryName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "acrAdminUserEnabled": { + "value": "[parameters('acrAdminUserEnabled')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "acrSku": { + "value": "[parameters('acrSku')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8799580877381308457" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "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 + }, + "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 + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "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." + } + } + } + }, + "nullable": true + }, + "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, using 'latest'." + } + }, + "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." + } + } + }, + "nullable": true + }, + "scopeMapsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of your Azure container registry." + } + }, + "exportPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the export policy is enabled or not." + } + }, + "quarantinePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "trustPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the retention policy is enabled or not." + } + }, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, + "metadata": { + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." + } + }, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled." + } + }, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. Soft Delete policy status. Default is disabled." + } + }, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "allowedValues": [ + "AzureServices", + "None" + ], + "metadata": { + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." + } + }, + "networkRuleSetDefaultAction": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. The default action of allow or deny when no other rules match." + } + }, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + }, + "replications": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All replications to create." + } + }, + "webhooks": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All webhooks to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "cacheRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of Cache Rules." + } + }, + "credentialSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Credential Sets." + } + }, + "scopeMaps": { + "$ref": "#/definitions/scopeMapsType", + "metadata": { + "description": "Optional. Scope maps setting." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.containerregistry-registry.{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" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, + "properties": { + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } + }, + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "registry_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { + "copy": { + "name": "registry_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "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": [ + "registry" + ] + }, + "registry_roleAssignments": { + "copy": { + "name": "registry_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "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": [ + "registry" + ] + }, + "registry_scopeMaps": { + "copy": { + "name": "registry_scopeMaps", + "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" + }, + "actions": { + "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" + }, + "registryName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9144531012597082524" + }, + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8531695368487734118" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_credentialSets": { + "copy": { + "name": "registry_credentialSets", + "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "managedIdentities": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" + }, + "authCredentials": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" + }, + "loginServer": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12196074162662855376" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + } + }, + "authCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + } + } + } + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Required. The managed identity definition for this resource." + } + }, + "authCredentials": { + "$ref": "#/definitions/authCredentialsType", + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Credential Set." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Credential Set." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "registryName": { + "value": "[parameters('name')]" + }, + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name'), replace(replace(coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository, '/', '-'), '.', '-'))]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4294329625336671928" + }, + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", + "metadata": { + "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Cache Rule." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Cache Rule." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Cache Rule." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry", + "registry_credentialSets" + ] + }, + "registry_webhooks": { + "copy": { + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" + }, + "action": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action'), createArray('chart_delete', 'chart_push', 'delete', 'push', 'quarantine'))]" + }, + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" + }, + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" + }, + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" + }, + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14912363209364245195" + }, + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the webhook." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { + "type": "string", + "metadata": { + "description": "The status of the webhook." + }, + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "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.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "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", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "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." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "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": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{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" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-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(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "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": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. 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": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "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-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + }, + "credentialSetsSystemAssignedMIPrincipalIds": { + "type": "array", + "metadata": { + "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.systemAssignedMIPrincipalId.value]" + } + }, + "credentialSetsResourceIds": { + "type": "array", + "metadata": { + "description": "The Resource IDs of the ACR Credential Sets." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.resourceId.value]" + } + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the all resources was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "defaultDomain": { + "type": "string", + "metadata": { + "description": "The Default domain of the Managed Environment." + }, + "value": "[reference('containerAppsEnvironment').outputs.defaultDomain.value]" + }, + "environmentName": { + "type": "string", + "metadata": { + "description": "The name of the Managed Environment." + }, + "value": "[reference('containerAppsEnvironment').outputs.name.value]" + }, + "environmentResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Managed Environment." + }, + "value": "[reference('containerAppsEnvironment').outputs.resourceId.value]" + }, + "registryLoginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference('containerRegistry').outputs.loginServer.value]" + }, + "registryName": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[reference('containerRegistry').outputs.name.value]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/dependencies.bicep b/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/dependencies.bicep new file mode 100644 index 0000000000..4735570581 --- /dev/null +++ b/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/dependencies.bicep @@ -0,0 +1,58 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location + properties: { + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + delegations: [ + { + name: 'Microsoft.App.environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/main.test.bicep b/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/main.test.bicep new file mode 100644 index 0000000000..19ec8ef14a --- /dev/null +++ b/avm/ptn/azd/container-apps/tests/e2e/zone-redundant/main.test.bicep @@ -0,0 +1,75 @@ +targetScope = 'subscription' + +metadata name = 'With zoneRedundant enabled' +metadata description = 'This instance deploys the module with zoneRedundant enabled.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-container-apps-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'acazr' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + containerAppsEnvironmentName: '${namePrefix}${serviceShort}cae001' + containerRegistryName: '${namePrefix}${serviceShort}cr001' + logAnalyticsWorkspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + location: resourceLocation + acrSku: 'Standard' + zoneRedundant: true + workloadProfiles: [ + { + workloadProfileType: 'D4' + name: 'CAW01' + minimumCount: 0 + maximumCount: 3 + } + ] + internal: true + dockerBridgeCidr: '172.16.0.1/28' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + infrastructureSubnetResourceId: nestedDependencies.outputs.subnetResourceId + infrastructureResourceGroupName: 'me-${resourceGroupName}' + } + } +] diff --git a/avm/ptn/azd/container-apps/version.json b/avm/ptn/azd/container-apps/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/azd/container-apps/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/ptn/deployment-script/import-image-to-acr/README.md b/avm/ptn/deployment-script/import-image-to-acr/README.md index d62504460f..4da7c30a06 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/README.md +++ b/avm/ptn/deployment-script/import-image-to-acr/README.md @@ -115,6 +115,7 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr cleanupPreference: 'OnExpiration' location: '' managedIdentities: '' + newImageName: 'your-image-name:tag' overwriteExistingImage: true storageAccountResourceId: '' subnetResourceIds: '' @@ -161,6 +162,9 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr "managedIdentities": { "value": "" }, + "newImageName": { + "value": "your-image-name:tag" + }, "overwriteExistingImage": { "value": true }, @@ -274,6 +278,7 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`initialScriptDelay`](#parameter-initialscriptdelay) | int | A delay in seconds before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate. Default is 30s. | | [`location`](#parameter-location) | string | Location for all Resources. | +| [`newImageName`](#parameter-newimagename) | string | The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline. | | [`overwriteExistingImage`](#parameter-overwriteexistingimage) | bool | The image will be overwritten if it already exists in the ACR with the same tag. Default is false. | | [`retryMax`](#parameter-retrymax) | int | The maximum number of retries for the script import operation. Default is 3. | | [`runOnce`](#parameter-runonce) | bool | How the deployment script should be forced to execute. Default is to force the script to deploy the image to run every time. | @@ -378,6 +383,15 @@ Location for all Resources. - Type: string - Default: `[resourceGroup().location]` +### Parameter: `newImageName` + +The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline. + +- Required: No +- Type: string +- Default: `[last(split(parameters('image'), '/'))]` +- Example: `your-image-name:tag` + ### Parameter: `overwriteExistingImage` The image will be overwritten if it already exists in the ACR with the same tag. Default is false. @@ -450,10 +464,12 @@ This section gives you an overview of all local-referenced module files (i.e., o ## Notes -The deployment script service will need and provision a Storage Account as well as a Container Instance to execute the provided script. *The deployment script resource is available only in the regions where Azure Container Instances is available.* +The deployment script service will need and provision a Storage Account as well as a Container Instance to execute the provided script. _The deployment script resource is available only in the regions where Azure Container Instances is available._ > The service cleans up these resources after the deployment script finishes. You incur charges for these resources until they're removed. +### Private network access + Using a Container Registry that is not available via public network access is possible as well. In this case a subnet needs to be passed to the module. A working configuration is in the max examples for this module. Links: @@ -461,6 +477,12 @@ Links: - [Access a private virtual network from a Bicep deployment script](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet) - [Run Bicep deployment script privately over a private endpoint](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet-private-endpoint) +### Renaming the image + +In the case an image is updated later from a pipeline, it can makes sense to initially upload a dummy image from e.g. a public registry. However, you might want to change the name and tag from that image for processes to pick up the image before **your image** has actually be published to the registry. + +The pattern module [avm/ptn/app/container-job](https://github.com/Azure/bicep-registry-modules/tree/main/avm/ptn/authorization/policy-assignment) uses this module to create the job initially with an image that is available, which most likely will be updated later and the job will pick up the new image then. + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.bicep b/avm/ptn/deployment-script/import-image-to-acr/main.bicep index 666fb79163..eb578a27a1 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/main.bicep @@ -32,6 +32,12 @@ param managedIdentityName string? }) param image string +@description('Optional. The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline.') +@metadata({ + example: 'your-image-name:tag' +}) +param newImageName string = last(split(image, '/')) + @description('Optional. The image will be overwritten if it already exists in the ACR with the same tag. Default is false.') param overwriteExistingImage bool = false @@ -166,6 +172,10 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { name: 'imageName' value: image } + { + name: 'newImageName' + value: newImageName + } { name: 'overwriteExistingImage' value: toLower(string(overwriteExistingImage)) @@ -200,9 +210,9 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { do echo "Importing Image ($retryLoopCount): $imageName into ACR: $acrName\n" if [ $overwriteExistingImage = 'true' ]; then - az acr import -n $acrName --source $imageName --force + az acr import -n $acrName --source $imageName --image $newImageName --force else - az acr import -n $acrName --source $imageName + az acr import -n $acrName --source $imageName --image $newImageName fi sleep $retrySleep diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.json b/avm/ptn/deployment-script/import-image-to-acr/main.json index 5add360aec..ffc946ccc3 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.json +++ b/avm/ptn/deployment-script/import-image-to-acr/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.28.1.47646", - "templateHash": "723991129039360172" + "templateHash": "18410876545978102921" }, "name": "import-image-to-acr", "description": "This modules deployes an image to an Azure Container Registry.", @@ -107,6 +107,14 @@ "description": "Required. A fully qualified image name to import." } }, + "newImageName": { + "type": "string", + "defaultValue": "[last(split(parameters('image'), '/'))]", + "metadata": { + "example": "your-image-name:tag", + "description": "Optional. The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline." + } + }, "overwriteExistingImage": { "type": "bool", "defaultValue": false, @@ -300,6 +308,10 @@ "name": "imageName", "value": "[parameters('image')]" }, + { + "name": "newImageName", + "value": "[parameters('newImageName')]" + }, { "name": "overwriteExistingImage", "value": "[toLower(string(parameters('overwriteExistingImage')))]" @@ -332,7 +344,7 @@ "value": "[parameters('subnetResourceIds')]" }, "scriptContent": { - "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n az acr import -n $acrName --source $imageName --force\n else\n az acr import -n $acrName --source $imageName\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" + "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" } }, "template": { diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep index 5a825c58aa..a00d32d62c 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep @@ -60,6 +60,7 @@ module testDeployment '../../../main.bicep' = [ acrName: dependencies.outputs.acrName location: resourceLocation image: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + newImageName: 'your-image-name:tag' cleanupPreference: 'OnExpiration' assignRbacRole: true managedIdentities: { userAssignedResourcesIds: [dependencies.outputs.managedIdentityResourceId] } diff --git a/avm/ptn/deployment-script/import-image-to-acr/version.json b/avm/ptn/deployment-script/import-image-to-acr/version.json index 8def869ede..daf1a794d9 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/version.json +++ b/avm/ptn/deployment-script/import-image-to-acr/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/ptn/finops-toolkit/finops-hub/main.bicep b/avm/ptn/finops-toolkit/finops-hub/main.bicep index c96c07fcce..68a4bb56d2 100644 --- a/avm/ptn/finops-toolkit/finops-hub/main.bicep +++ b/avm/ptn/finops-toolkit/finops-hub/main.bicep @@ -99,10 +99,7 @@ module storage 'modules/storage.bicep' = { resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' = { name: dataFactoryName location: location - tags: union( - resourceTags, - contains(tagsByResource, 'Microsoft.DataFactory/factories') ? tagsByResource['Microsoft.DataFactory/factories'] : {} - ) + tags: union(resourceTags, tagsByResource[?'Microsoft.DataFactory/factories'] ?? {}) identity: { type: 'SystemAssigned' } properties: any( // Using any() to hide the error that gets surfaced because globalConfigurations is not in the ADF schema yet. @@ -174,6 +171,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } +#disable-next-line no-deployments-resources resource defaultTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { name: 'pid-${telemetryId}-${uniqueString(deployment().name, location)}' properties: { diff --git a/avm/ptn/finops-toolkit/finops-hub/main.json b/avm/ptn/finops-toolkit/finops-hub/main.json index e8fb5815b2..3cf86b2d5e 100644 --- a/avm/ptn/finops-toolkit/finops-hub/main.json +++ b/avm/ptn/finops-toolkit/finops-hub/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13514907472495855174" + "version": "0.29.47.4906", + "templateHash": "2378205016082649078" }, "name": "Finops-hub", "description": "This module deploys a Finops hub from the Finops toolkit.", @@ -112,7 +112,7 @@ "apiVersion": "2018-06-01", "name": "[variables('dataFactoryName')]", "location": "[parameters('location')]", - "tags": "[union(variables('resourceTags'), if(contains(parameters('tagsByResource'), 'Microsoft.DataFactory/factories'), parameters('tagsByResource')['Microsoft.DataFactory/factories'], createObject()))]", + "tags": "[union(variables('resourceTags'), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.DataFactory/factories'), createObject()))]", "identity": { "type": "SystemAssigned" }, @@ -210,8 +210,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7548608522101382905" + "version": "0.29.47.4906", + "templateHash": "4568813132024947706" } }, "parameters": { @@ -291,14 +291,14 @@ } }, "variables": { - "$fxv#0": "Write-Output 'Updating settings.json file...'\r\nWrite-Output \"Storage account: $env:storageAccountName\"\r\nWrite-Output \"Container: $env:containerName\"\r\n\r\n$validateScopes = { $_.Length -gt 45 }\r\n\r\n# Initialize variables\r\n$fileName = 'settings.json'\r\n$filePath = Join-Path -Path . -ChildPath $fileName\r\n$newScopes = $env:exportScopes.Split('|') | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\r\n\r\n# Get storage context\r\n$storageContext = @{\r\n Context = New-AzStorageContext -StorageAccountName $env:storageAccountName -UseConnectedAccount\r\n Container = $env:containerName\r\n}\r\n\r\n# Download existing settings, if they exist\r\n$blob = Get-AzStorageBlobContent @storageContext -Blob $fileName -Destination $filePath -Force\r\nif ($blob) {\r\n Write-Output 'Existing settings.json file found. Updating...'\r\n $text = Get-Content $filePath -Raw\r\n Write-Output '---------'\r\n Write-Output $text\r\n Write-Output '---------'\r\n $json = $text | ConvertFrom-Json\r\n\r\n # Rename exportScopes to scopes + convert to object array\r\n if ($json.exportScopes) {\r\n Write-Output ' Updating exportScopes...'\r\n if ($json.exportScopes[0] -is [string]) {\r\n Write-Output ' Converting string array to object array...'\r\n $json.exportScopes = $json.exportScopes | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\r\n if (-not ($json.exportScopes -is [array])) {\r\n Write-Output ' Converting single object to object array...'\r\n $json.exportScopes = @($json.exportScopes)\r\n }\r\n }\r\n\r\n Write-Output \" Renaming to 'scopes'...\"\r\n $json | Add-Member -MemberType NoteProperty -Name scopes -Value $json.exportScopes\r\n $json.PSObject.Properties.Remove('exportScopes')\r\n }\r\n}\r\n\r\n# Set default if not found\r\nif (!$json) {\r\n Write-Output 'No existing settings.json file found. Creating new file...'\r\n $json = [ordered]@{\r\n '$schema' = 'https://aka.ms/finops/hubs/settings-schema'\r\n type = 'HubInstance'\r\n version = ''\r\n learnMore = 'https://aka.ms/finops/hubs'\r\n scopes = @()\r\n }\r\n}\r\n\r\n# Updating settings\r\nWrite-Output \"Updating version to $env:ftkVersion...\"\r\n$json.version = $env:ftkVersion\r\nif ($newScopes) {\r\n Write-Output \"Merging $($newScopes.Count) scopes...\"\r\n $json.scopes = Compare-Object -ReferenceObject $json.scopes -DifferenceObject $newScopes -Property scope -PassThru -IncludeEqual\r\n\r\n # Remove the SideIndicator property from the Compare-Object output\r\n $json.scopes | ForEach-Object { $_.PSObject.Properties.Remove('SideIndicator') } | ConvertTo-Json\r\n\r\n if (-not ($json.scopes -is [array])) {\r\n $json.scopes = @($json.scopes)\r\n }\r\n Write-Output \"$($json.scopes.Count) scopes found.\"\r\n}\r\n$text = $json | ConvertTo-Json\r\nWrite-Output '---------'\r\nWrite-Output $text\r\nWrite-Output '---------'\r\n$text | Out-File $filePath\r\n\r\n# Upload new/updated settings\r\nWrite-Output 'Uploading settings.json file...'\r\nSet-AzStorageBlobContent @storageContext -File $filePath -Force\r\n" + "$fxv#0": "Write-Output 'Updating settings.json file...'\nWrite-Output \"Storage account: $env:storageAccountName\"\nWrite-Output \"Container: $env:containerName\"\n\n$validateScopes = { $_.Length -gt 45 }\n\n# Initialize variables\n$fileName = 'settings.json'\n$filePath = Join-Path -Path . -ChildPath $fileName\n$newScopes = $env:exportScopes.Split('|') | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\n\n# Get storage context\n$storageContext = @{\n Context = New-AzStorageContext -StorageAccountName $env:storageAccountName -UseConnectedAccount\n Container = $env:containerName\n}\n\n# Download existing settings, if they exist\n$blob = Get-AzStorageBlobContent @storageContext -Blob $fileName -Destination $filePath -Force\nif ($blob) {\n Write-Output 'Existing settings.json file found. Updating...'\n $text = Get-Content $filePath -Raw\n Write-Output '---------'\n Write-Output $text\n Write-Output '---------'\n $json = $text | ConvertFrom-Json\n\n # Rename exportScopes to scopes + convert to object array\n if ($json.exportScopes) {\n Write-Output ' Updating exportScopes...'\n if ($json.exportScopes[0] -is [string]) {\n Write-Output ' Converting string array to object array...'\n $json.exportScopes = $json.exportScopes | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\n if (-not ($json.exportScopes -is [array])) {\n Write-Output ' Converting single object to object array...'\n $json.exportScopes = @($json.exportScopes)\n }\n }\n\n Write-Output \" Renaming to 'scopes'...\"\n $json | Add-Member -MemberType NoteProperty -Name scopes -Value $json.exportScopes\n $json.PSObject.Properties.Remove('exportScopes')\n }\n}\n\n# Set default if not found\nif (!$json) {\n Write-Output 'No existing settings.json file found. Creating new file...'\n $json = [ordered]@{\n '$schema' = 'https://aka.ms/finops/hubs/settings-schema'\n type = 'HubInstance'\n version = ''\n learnMore = 'https://aka.ms/finops/hubs'\n scopes = @()\n }\n}\n\n# Updating settings\nWrite-Output \"Updating version to $env:ftkVersion...\"\n$json.version = $env:ftkVersion\nif ($newScopes) {\n Write-Output \"Merging $($newScopes.Count) scopes...\"\n $json.scopes = Compare-Object -ReferenceObject $json.scopes -DifferenceObject $newScopes -Property scope -PassThru -IncludeEqual\n\n # Remove the SideIndicator property from the Compare-Object output\n $json.scopes | ForEach-Object { $_.PSObject.Properties.Remove('SideIndicator') } | ConvertTo-Json\n\n if (-not ($json.scopes -is [array])) {\n $json.scopes = @($json.scopes)\n }\n Write-Output \"$($json.scopes.Count) scopes found.\"\n}\n$text = $json | ConvertTo-Json\nWrite-Output '---------'\nWrite-Output $text\nWrite-Output '---------'\n$text | Out-File $filePath\n\n# Upload new/updated settings\nWrite-Output 'Uploading settings.json file...'\nSet-AzStorageBlobContent @storageContext -File $filePath -Force\n" }, "resources": { "identity": { "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2023-01-31", "name": "[format('{0}_blobManager', parameters('storageAccountName'))]", - "tags": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tagsByResource')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()))]", + "tags": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), createObject()))]", "location": "[parameters('location')]" }, "storageAccount": { @@ -321,7 +321,7 @@ "value": "BlockBlobStorage" }, "tags": { - "value": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.Storage/storageAccounts'), parameters('tagsByResource')['Microsoft.Storage/storageAccounts'], createObject()))]" + "value": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.Storage/storageAccounts'), createObject()))]" }, "supportsHttpsTrafficOnly": { "value": true @@ -5027,7 +5027,7 @@ }, "location": "[if(startsWith(parameters('location'), 'china'), createObject('value', 'chinaeast2'), createObject('value', parameters('location')))]", "tags": { - "value": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]" + "value": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), createObject()))]" }, "managedIdentities": { "value": { @@ -5614,8 +5614,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13824362730485492490" + "version": "0.29.47.4906", + "templateHash": "17793535838056076246" } }, "parameters": { @@ -5699,9 +5699,9 @@ } } ], - "$fxv#0": "# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\n$adfParams = @{\r\n ResourceGroupName = $env:DataFactoryResourceGroup\r\n DataFactoryName = $env:DataFactoryName\r\n}\r\n\r\n# Delete old triggers\r\n$triggers = Get-AzDataFactoryV2Trigger @adfParams -ErrorAction SilentlyContinue `\r\n| Where-Object { $_.Name -match '^msexports?$' }\r\n$DeploymentScriptOutputs['stopTriggers'] = $triggers | Stop-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\r\n$DeploymentScriptOutputs['deleteTriggers'] = $triggers | Remove-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\r\n\r\n# Delete old pipelines\r\n$DeploymentScriptOutputs['pipelines'] = Get-AzDataFactoryV2Pipeline @adfParams -ErrorAction SilentlyContinue `\r\n| Where-Object { $_.Name -match '^msexports_(extract|transform)$' } `\r\n| Remove-AzDataFactoryV2Pipeline -Force -ErrorAction SilentlyContinue\r\n", - "$fxv#1": "Param(\r\n [switch] $Stop\r\n)\r\n# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\nif (-not $Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n\r\n# Loop through triggers\r\n$env:Triggers.Split('|') `\r\n| ForEach-Object {\r\n $trigger = $_\r\n if ($Stop) {\r\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\r\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force `\r\n -ErrorAction SilentlyContinue\r\n } else {\r\n Write-Host \"Starting trigger $trigger...\" -NoNewline\r\n $triggerOutput = Start-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force\r\n }\r\n if ($triggerOutput) {\r\n Write-Host 'done'\r\n } else {\r\n Write-Host 'failed'\r\n }\r\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\r\n}\r\n\r\nif ($Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n", - "$fxv#2": "Param(\r\n [switch] $Stop\r\n)\r\n# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\nif (-not $Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n\r\n# Loop through triggers\r\n$env:Triggers.Split('|') `\r\n| ForEach-Object {\r\n $trigger = $_\r\n if ($Stop) {\r\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\r\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force `\r\n -ErrorAction SilentlyContinue\r\n } else {\r\n Write-Host \"Starting trigger $trigger...\" -NoNewline\r\n $triggerOutput = Start-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force\r\n }\r\n if ($triggerOutput) {\r\n Write-Host 'done'\r\n } else {\r\n Write-Host 'failed'\r\n }\r\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\r\n}\r\n\r\nif ($Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n", + "$fxv#0": "# Init outputs\n$DeploymentScriptOutputs = @{}\n\n$adfParams = @{\n ResourceGroupName = $env:DataFactoryResourceGroup\n DataFactoryName = $env:DataFactoryName\n}\n\n# Delete old triggers\n$triggers = Get-AzDataFactoryV2Trigger @adfParams -ErrorAction SilentlyContinue `\n| Where-Object { $_.Name -match '^msexports?$' }\n$DeploymentScriptOutputs['stopTriggers'] = $triggers | Stop-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\n$DeploymentScriptOutputs['deleteTriggers'] = $triggers | Remove-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\n\n# Delete old pipelines\n$DeploymentScriptOutputs['pipelines'] = Get-AzDataFactoryV2Pipeline @adfParams -ErrorAction SilentlyContinue `\n| Where-Object { $_.Name -match '^msexports_(extract|transform)$' } `\n| Remove-AzDataFactoryV2Pipeline -Force -ErrorAction SilentlyContinue\n", + "$fxv#1": "Param(\n [switch] $Stop\n)\n# Init outputs\n$DeploymentScriptOutputs = @{}\n\nif (-not $Stop) {\n Start-Sleep -Seconds 10\n}\n\n# Loop through triggers\n$env:Triggers.Split('|') `\n| ForEach-Object {\n $trigger = $_\n if ($Stop) {\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\n -ResourceGroupName $env:DataFactoryResourceGroup `\n -DataFactoryName $env:DataFactoryName `\n -Name $trigger `\n -Force `\n -ErrorAction SilentlyContinue\n } else {\n Write-Host \"Starting trigger $trigger...\" -NoNewline\n $triggerOutput = Start-AzDataFactoryV2Trigger `\n -ResourceGroupName $env:DataFactoryResourceGroup `\n -DataFactoryName $env:DataFactoryName `\n -Name $trigger `\n -Force\n }\n if ($triggerOutput) {\n Write-Host 'done'\n } else {\n Write-Host 'failed'\n }\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\n}\n\nif ($Stop) {\n Start-Sleep -Seconds 10\n}\n", + "$fxv#2": "Param(\n [switch] $Stop\n)\n# Init outputs\n$DeploymentScriptOutputs = @{}\n\nif (-not $Stop) {\n Start-Sleep -Seconds 10\n}\n\n# Loop through triggers\n$env:Triggers.Split('|') `\n| ForEach-Object {\n $trigger = $_\n if ($Stop) {\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\n -ResourceGroupName $env:DataFactoryResourceGroup `\n -DataFactoryName $env:DataFactoryName `\n -Name $trigger `\n -Force `\n -ErrorAction SilentlyContinue\n } else {\n Write-Host \"Starting trigger $trigger...\" -NoNewline\n $triggerOutput = Start-AzDataFactoryV2Trigger `\n -ResourceGroupName $env:DataFactoryResourceGroup `\n -DataFactoryName $env:DataFactoryName `\n -Name $trigger `\n -Force\n }\n if ($triggerOutput) {\n Write-Host 'done'\n } else {\n Write-Host 'failed'\n }\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\n}\n\nif ($Stop) {\n Start-Sleep -Seconds 10\n}\n", "datasetPropsDelimitedText": { "columnDelimiter": ",", "compressionLevel": "Optimal", @@ -6123,7 +6123,7 @@ "apiVersion": "2023-01-31", "name": "[format('{0}_triggerManager', parameters('dataFactoryName'))]", "location": "[parameters('location')]", - "tags": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tagsByResource')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()))]" + "tags": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), createObject()))]" }, "identityRoleAssignments": { "copy": { @@ -7235,7 +7235,7 @@ "value": "AzurePowerShell" }, "tags": { - "value": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]" + "value": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), createObject()))]" }, "azPowerShellVersion": { "value": "9.7" @@ -7782,7 +7782,7 @@ "value": "[parameters('location')]" }, "tags": { - "value": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]" + "value": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), createObject()))]" }, "kind": { "value": "AzurePowerShell" @@ -8386,8 +8386,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2821773295072797810" + "version": "0.29.47.4906", + "templateHash": "2016642433151139575" } }, "parameters": { @@ -8457,10 +8457,10 @@ "name": "formattedAccessPolicies", "count": "[length(parameters('accessPolicies'))]", "input": { - "applicationId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].applicationId, '')]", - "objectId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].objectId, '')]", + "applicationId": "[coalesce(tryGet(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), '')]", + "objectId": "[coalesce(tryGet(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), '')]", "permissions": "[parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].permissions]", - "tenantId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].tenantId, tenant().tenantId)]" + "tenantId": "[coalesce(tryGet(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), tenant().tenantId)]" } } ], @@ -8492,7 +8492,7 @@ "value": "[parameters('location')]" }, "tags": { - "value": "[union(coalesce(parameters('tags'), createObject()), if(contains(parameters('tagsByResource'), 'Microsoft.KeyVault/vaults'), parameters('tagsByResource')['Microsoft.KeyVault/vaults'], createObject()))]" + "value": "[union(coalesce(parameters('tags'), createObject()), coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.KeyVault/vaults'), createObject()))]" }, "enableVaultForDeployment": { "value": true diff --git a/avm/ptn/finops-toolkit/finops-hub/modules/dataFactory.bicep b/avm/ptn/finops-toolkit/finops-hub/modules/dataFactory.bicep index 3e4a79fdbe..61b21125d1 100644 --- a/avm/ptn/finops-toolkit/finops-hub/modules/dataFactory.bicep +++ b/avm/ptn/finops-toolkit/finops-hub/modules/dataFactory.bicep @@ -232,12 +232,7 @@ module deleteOldResources 'br/public:avm/res/resources/deployment-script:0.2.0' resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: '${dataFactoryName}_triggerManager' location: location - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.ManagedIdentity/userAssignedIdentities') - ? tagsByResource['Microsoft.ManagedIdentity/userAssignedIdentities'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.ManagedIdentity/userAssignedIdentities'] ?? {}) } // Assign access to the identity @@ -262,12 +257,7 @@ module stopHubTriggers 'br/public:avm/res/resources/deployment-script:0.2.0' = { name: 'stopHubTriggers' location: location kind: 'AzurePowerShell' - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.Resources/deploymentScripts') - ? tagsByResource['Microsoft.Resources/deploymentScripts'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.Resources/deploymentScripts'] ?? {}) azPowerShellVersion: '9.7' retentionInterval: 'PT1H' cleanupPreference: 'OnSuccess' @@ -875,12 +865,7 @@ module startHubTriggers 'br/public:avm/res/resources/deployment-script:0.2.0' = params: { name: 'startHubTriggers' location: location - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.Resources/deploymentScripts') - ? tagsByResource['Microsoft.Resources/deploymentScripts'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.Resources/deploymentScripts'] ?? {}) kind: 'AzurePowerShell' azPowerShellVersion: '9.7' retentionInterval: 'PT1H' diff --git a/avm/ptn/finops-toolkit/finops-hub/modules/keyVault.bicep b/avm/ptn/finops-toolkit/finops-hub/modules/keyVault.bicep index 207d944432..491d92b8ef 100644 --- a/avm/ptn/finops-toolkit/finops-hub/modules/keyVault.bicep +++ b/avm/ptn/finops-toolkit/finops-hub/modules/keyVault.bicep @@ -41,10 +41,10 @@ var keyVaultName = replace('${take(keyVaultPrefix, 24 - length(keyVaultSuffix))} var formattedAccessPolicies = [ for accessPolicy in accessPolicies: { - applicationId: contains(accessPolicy, 'applicationId') ? accessPolicy.applicationId : '' - objectId: contains(accessPolicy, 'objectId') ? accessPolicy.objectId : '' + applicationId: accessPolicy.?applicationId ?? '' + objectId: accessPolicy.?objectId ?? '' permissions: accessPolicy.permissions - tenantId: contains(accessPolicy, 'tenantId') ? accessPolicy.tenantId : tenant().tenantId + tenantId: accessPolicy.?tenantId ?? tenant().tenantId } ] @@ -57,10 +57,7 @@ module keyVault 'br/public:avm/res/key-vault/vault:0.5.1' = { params: { name: keyVaultName location: location - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.KeyVault/vaults') ? tagsByResource['Microsoft.KeyVault/vaults'] : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.KeyVault/vaults'] ?? {}) enableVaultForDeployment: true enableVaultForTemplateDeployment: true enableVaultForDiskEncryption: true diff --git a/avm/ptn/finops-toolkit/finops-hub/modules/storage.bicep b/avm/ptn/finops-toolkit/finops-hub/modules/storage.bicep index 7189586b6e..83d7547297 100644 --- a/avm/ptn/finops-toolkit/finops-hub/modules/storage.bicep +++ b/avm/ptn/finops-toolkit/finops-hub/modules/storage.bicep @@ -45,12 +45,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:0.8.3' = { name: storageAccountName skuName: sku kind: 'BlockBlobStorage' - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.Storage/storageAccounts') - ? tagsByResource['Microsoft.Storage/storageAccounts'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.Storage/storageAccounts'] ?? {}) supportsHttpsTrafficOnly: true minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false @@ -101,12 +96,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:0.8.3' = { // Create managed identity to upload files resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: '${storageAccountName}_blobManager' - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.ManagedIdentity/userAssignedIdentities') - ? tagsByResource['Microsoft.ManagedIdentity/userAssignedIdentities'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.ManagedIdentity/userAssignedIdentities'] ?? {}) location: location } @@ -116,12 +106,7 @@ module uploadSettings 'br/public:avm/res/resources/deployment-script:0.2.0' = { name: 'uploadSettings' kind: 'AzurePowerShell' location: startsWith(location, 'china') ? 'chinaeast2' : location - tags: union( - tags ?? {}, - contains(tagsByResource, 'Microsoft.Resources/deploymentScripts') - ? tagsByResource['Microsoft.Resources/deploymentScripts'] - : {} - ) + tags: union(tags ?? {}, tagsByResource[?'Microsoft.Resources/deploymentScripts'] ?? {}) managedIdentities: { userAssignedResourcesIds: [ identity.id diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 9d90d9d141..99875cc023 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -663,6 +663,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -713,6 +714,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/aad/domain-service/main.bicep b/avm/res/aad/domain-service/main.bicep index 6576afc7d4..269ddc64df 100644 --- a/avm/res/aad/domain-service/main.bicep +++ b/avm/res/aad/domain-service/main.bicep @@ -176,6 +176,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -272,14 +283,10 @@ resource domainservice_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!em } resource domainservice_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(domainservice.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(domainservice.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -320,6 +327,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json index af3fe98798..163ff3d3f1 100644 --- a/avm/res/aad/domain-service/main.json +++ b/avm/res/aad/domain-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12624521298275316685" + "version": "0.29.47.4906", + "templateHash": "13221664498320911695" }, "name": "Azure Active Directory Domain Services", "description": "This module deploys an Azure Active Directory Domain Services (AADDS) instance.", @@ -43,6 +43,13 @@ "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": { @@ -479,6 +486,13 @@ } }, "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')]", @@ -599,20 +613,20 @@ "domainservice_roleAssignments": { "copy": { "name": "domainservice_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.AAD/domainServices/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.AAD/domainServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AAD/domainServices', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "domainservice" diff --git a/avm/res/aad/domain-service/version.json b/avm/res/aad/domain-service/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/aad/domain-service/version.json +++ b/avm/res/aad/domain-service/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/alerts-management/action-rule/README.md b/avm/res/alerts-management/action-rule/README.md index f952455200..5f69ffab62 100644 --- a/avm/res/alerts-management/action-rule/README.md +++ b/avm/res/alerts-management/action-rule/README.md @@ -199,11 +199,13 @@ module actionRule 'br/public:avm/res/alerts-management/action-rule:' = } roleAssignments: [ { + name: 'a66da6bc-b3ee-484e-9bdb-9294938bb327' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -360,11 +362,13 @@ module actionRule 'br/public:avm/res/alerts-management/action-rule:' = "roleAssignments": { "value": [ { + "name": "a66da6bc-b3ee-484e-9bdb-9294938bb327", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -617,6 +621,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -667,6 +672,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/alerts-management/action-rule/main.bicep b/avm/res/alerts-management/action-rule/main.bicep index 1b61eea13f..ba9ba4553a 100644 --- a/avm/res/alerts-management/action-rule/main.bicep +++ b/avm/res/alerts-management/action-rule/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.alertsmanagement-actionrule.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -103,14 +114,10 @@ resource actionRule_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty } resource actionRule_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(actionRule.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(actionRule.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -143,6 +150,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/alerts-management/action-rule/main.json b/avm/res/alerts-management/action-rule/main.json index 3211a650cc..abf1034b21 100644 --- a/avm/res/alerts-management/action-rule/main.json +++ b/avm/res/alerts-management/action-rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14201417983604554877" + "version": "0.29.47.4906", + "templateHash": "2785237563676416339" }, "name": "Action Rules", "description": "This module deploys an Alert Processing Rule.", @@ -43,6 +43,13 @@ "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": { @@ -195,6 +202,13 @@ } }, "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')]", @@ -256,20 +270,20 @@ "actionRule_roleAssignments": { "copy": { "name": "actionRule_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.AlertsManagement/actionRules/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.AlertsManagement/actionRules', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AlertsManagement/actionRules', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "actionRule" diff --git a/avm/res/alerts-management/action-rule/tests/e2e/max/main.test.bicep b/avm/res/alerts-management/action-rule/tests/e2e/max/main.test.bicep index cc7789d93f..ca4f5b8ab9 100644 --- a/avm/res/alerts-management/action-rule/tests/e2e/max/main.test.bicep +++ b/avm/res/alerts-management/action-rule/tests/e2e/max/main.test.bicep @@ -159,11 +159,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'a66da6bc-b3ee-484e-9bdb-9294938bb327' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/alerts-management/action-rule/version.json b/avm/res/alerts-management/action-rule/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/alerts-management/action-rule/version.json +++ b/avm/res/alerts-management/action-rule/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/analysis-services/server/README.md b/avm/res/analysis-services/server/README.md index 5d83348f2f..30cb104526 100644 --- a/avm/res/analysis-services/server/README.md +++ b/avm/res/analysis-services/server/README.md @@ -140,11 +140,13 @@ module server 'br/public:avm/res/analysis-services/server:' = { } roleAssignments: [ { + name: '0a657697-dd80-427e-b1bc-7970ab74f937' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -231,11 +233,13 @@ module server 'br/public:avm/res/analysis-services/server:' = { "roleAssignments": { "value": [ { + "name": "0a657697-dd80-427e-b1bc-7970ab74f937", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -683,6 +687,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -733,6 +738,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/analysis-services/server/main.bicep b/avm/res/analysis-services/server/main.bicep index c7c7001549..a7af3de63b 100644 --- a/avm/res/analysis-services/server/main.bicep +++ b/avm/res/analysis-services/server/main.bicep @@ -55,6 +55,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.analysisservices-server.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -128,14 +139,10 @@ resource server_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-0 ] resource server_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(server.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(server.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -172,6 +179,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/analysis-services/server/main.json b/avm/res/analysis-services/server/main.json index 94f7a83c43..3dcd5bbc8c 100644 --- a/avm/res/analysis-services/server/main.json +++ b/avm/res/analysis-services/server/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6732010316423918942" + "version": "0.29.47.4906", + "templateHash": "2303333571434821196" }, "name": "Analysis Services Servers", "description": "This module deploys an Analysis Services Server.", @@ -43,6 +43,13 @@ "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": { @@ -303,6 +310,13 @@ } }, "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')]", @@ -404,20 +418,20 @@ "server_roleAssignments": { "copy": { "name": "server_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.AnalysisServices/servers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.AnalysisServices/servers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AnalysisServices/servers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "server" diff --git a/avm/res/analysis-services/server/tests/e2e/max/main.test.bicep b/avm/res/analysis-services/server/tests/e2e/max/main.test.bicep index 36954fb7b2..f935420cc4 100644 --- a/avm/res/analysis-services/server/tests/e2e/max/main.test.bicep +++ b/avm/res/analysis-services/server/tests/e2e/max/main.test.bicep @@ -84,11 +84,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '0a657697-dd80-427e-b1bc-7970ab74f937' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/analysis-services/server/version.json b/avm/res/analysis-services/server/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/analysis-services/server/version.json +++ b/avm/res/analysis-services/server/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index a9c4633306..1a74dba710 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -1,6 +1,6 @@ # API Management Services `[Microsoft.ApiManagement/service]` -This module deploys an API Management Service. +This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU. ## Navigation @@ -16,7 +16,7 @@ This module deploys an API Management Service. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.ApiManagement/service` | [2023-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2023-05-01-preview/service) | +| `Microsoft.ApiManagement/service` | [2023-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/service) | | `Microsoft.ApiManagement/service/apis` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis) | | `Microsoft.ApiManagement/service/apis/diagnostics` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis/diagnostics) | | `Microsoft.ApiManagement/service/apis/policies` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis/policies) | @@ -45,12 +45,74 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/api-management/service:`. -- [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [Test deploying apim v2 sku](#example-3-test-deploying-apim-v2-sku) -- [WAF-aligned](#example-4-waf-aligned) +- [Deploying a Consumption SKU](#example-1-deploying-a-consumption-sku) +- [Using only defaults](#example-2-using-only-defaults) +- [Deploying a Developer SKU](#example-3-deploying-a-developer-sku) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [Deploying an APIM v2 sku](#example-5-deploying-an-apim-v2-sku) +- [WAF-aligned](#example-6-waf-aligned) -### Example 1: _Using only defaults_ +### Example 1: _Deploying a Consumption SKU_ + +This instance deploys the module using a Consumption SKU. + + +

+ +via Bicep module + +```bicep +module service 'br/public:avm/res/api-management/service:' = { + name: 'serviceDeployment' + params: { + // Required parameters + name: 'apiscon001' + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: 'az-amorg-x-001' + // Non-required parameters + location: '' + sku: 'Consumption' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apiscon001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "az-amorg-x-001" + }, + // Non-required parameters + "location": { + "value": "" + }, + "sku": { + "value": "Consumption" + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -106,7 +168,67 @@ module service 'br/public:avm/res/api-management/service:' = {

-### Example 2: _Using large parameter set_ +### Example 3: _Deploying a Developer SKU_ + +This instance deploys the module using a Developer SKU. + + +

+ +via Bicep module + +```bicep +module service 'br/public:avm/res/api-management/service:' = { + name: 'serviceDeployment' + params: { + // Required parameters + name: 'apisdev001' + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: 'az-amorg-x-001' + // Non-required parameters + location: '' + sku: 'Developer' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apisdev001" + }, + "publisherEmail": { + "value": "apimgmt-noreply@mail.windowsazure.com" + }, + "publisherName": { + "value": "az-amorg-x-001" + }, + // Non-required parameters + "location": { + "value": "" + }, + "sku": { + "value": "Developer" + } + } +} +``` + +
+

+ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -215,6 +337,7 @@ module service 'br/public:avm/res/api-management/service:' = { ] authority: '' clientId: 'apimClientid' + clientLibrary: 'MSAL-2' clientSecret: 'apimSlientSecret' name: 'aad' signinTenant: 'mytenant.onmicrosoft.com' @@ -294,11 +417,13 @@ module service 'br/public:avm/res/api-management/service:' = { publicIpAddressResourceId: '' roleAssignments: [ { + name: '6352c3e3-ac6b-43d5-ac43-1077ff373721' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -455,6 +580,7 @@ module service 'br/public:avm/res/api-management/service:' = { ], "authority": "", "clientId": "apimClientid", + "clientLibrary": "MSAL-2", "clientSecret": "apimSlientSecret", "name": "aad", "signinTenant": "mytenant.onmicrosoft.com" @@ -554,11 +680,13 @@ module service 'br/public:avm/res/api-management/service:' = { "roleAssignments": { "value": [ { + "name": "6352c3e3-ac6b-43d5-ac43-1077ff373721", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -598,9 +726,9 @@ module service 'br/public:avm/res/api-management/service:' = {

-### Example 3: _Test deploying apim v2 sku_ +### Example 5: _Deploying an APIM v2 sku_ -This instance deploys the module using a v2 SKU with the minimum set of required parameters. +This instance deploys the module using a v2 SKU.

@@ -658,7 +786,7 @@ module service 'br/public:avm/res/api-management/service:' = {

-### Example 4: _WAF-aligned_ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -767,6 +895,7 @@ module service 'br/public:avm/res/api-management/service:' = { ] authority: '' clientId: 'apimClientid' + clientLibrary: 'MSAL-2' clientSecret: '' name: 'aad' signinTenant: 'mytenant.onmicrosoft.com' @@ -987,6 +1116,7 @@ module service 'br/public:avm/res/api-management/service:' = { ], "authority": "", "clientId": "apimClientid", + "clientLibrary": "MSAL-2", "clientSecret": "", "name": "aad", "signinTenant": "mytenant.onmicrosoft.com" @@ -1113,6 +1243,12 @@ module service 'br/public:avm/res/api-management/service:' = { | [`publisherEmail`](#parameter-publisheremail) | string | The email address of the owner of the service. | | [`publisherName`](#parameter-publishername) | string | The name of the owner of the service. | +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`skuCapacity`](#parameter-skucapacity) | int | The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/. | + **Optional parameters** | Parameter | Type | Description | @@ -1125,7 +1261,7 @@ module service 'br/public:avm/res/api-management/service:' = { | [`backends`](#parameter-backends) | array | Backends. | | [`caches`](#parameter-caches) | array | Caches. | | [`certificates`](#parameter-certificates) | array | List of Certificates that need to be installed in the API Management service. Max supported certificates that can be installed is 10. | -| [`customProperties`](#parameter-customproperties) | object | Custom properties of the API Management service. | +| [`customProperties`](#parameter-customproperties) | object | Custom properties of the API Management service. Not supported if SKU is Consumption. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableGateway`](#parameter-disablegateway) | bool | Property only valid for an API Management service deployed in multiple locations. This can be used to disable the gateway in master region. | | [`enableClientCertificate`](#parameter-enableclientcertificate) | bool | Property only meant to be used for Consumption SKU Service. This enforces a client certificate to be presented on each request to the gateway. This also enables the ability to authenticate the certificate in the policy on the gateway. | @@ -1147,12 +1283,11 @@ module service 'br/public:avm/res/api-management/service:' = { | [`restore`](#parameter-restore) | bool | Undelete API Management Service if it was previously soft-deleted. If this flag is specified and set to True all other properties will be ignored. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`sku`](#parameter-sku) | string | The pricing tier of this API Management service. | -| [`skuCount`](#parameter-skucount) | int | The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0. | | [`subnetResourceId`](#parameter-subnetresourceid) | string | The full resource ID of a subnet in a virtual network to deploy the API Management service in. | | [`subscriptions`](#parameter-subscriptions) | array | Subscriptions. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`virtualNetworkType`](#parameter-virtualnetworktype) | string | The type of VPN in which API Management service needs to be configured in. None (Default Value) means the API Management service is not part of any Virtual Network, External means the API Management deployment is set up inside a Virtual Network having an internet Facing Endpoint, and Internal means that API Management deployment is setup inside a Virtual Network having an Intranet Facing Endpoint only. | -| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the resource needs to come from. Not supported with V2 SKUs. | +| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku. | ### Parameter: `name` @@ -1175,6 +1310,14 @@ The name of the owner of the service. - Required: Yes - Type: string +### Parameter: `skuCapacity` + +The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/. + +- Required: No +- Type: int +- Default: `2` + ### Parameter: `additionalLocations` Additional datacenter locations of the API Management service. Not supported with V2 SKUs. @@ -1241,7 +1384,7 @@ List of Certificates that need to be installed in the API Management service. Ma ### Parameter: `customProperties` -Custom properties of the API Management service. +Custom properties of the API Management service. Not supported if SKU is Consumption. - Required: No - Type: object @@ -1617,6 +1760,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1667,6 +1811,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1704,23 +1855,6 @@ The pricing tier of this API Management service. ] ``` -### Parameter: `skuCount` - -The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0. - -- Required: No -- Type: int -- Default: `2` -- Allowed: - ```Bicep - [ - 0 - 1 - 2 - 3 - ] - ``` - ### Parameter: `subnetResourceId` The full resource ID of a subnet in a virtual network to deploy the API Management service in. @@ -1761,7 +1895,7 @@ The type of VPN in which API Management service needs to be configured in. None ### Parameter: `zones` -A list of availability zones denoting where the resource needs to come from. Not supported with V2 SKUs. +A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku. - Required: No - Type: array diff --git a/avm/res/api-management/service/api-version-set/main.json b/avm/res/api-management/service/api-version-set/main.json index 128f641d75..85639acf5c 100644 --- a/avm/res/api-management/service/api-version-set/main.json +++ b/avm/res/api-management/service/api-version-set/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2869637630248924190" + "version": "0.29.47.4906", + "templateHash": "17159723717884761443" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set.", diff --git a/avm/res/api-management/service/api/diagnostics/main.json b/avm/res/api-management/service/api/diagnostics/main.json index 07dc670cb4..83e2b3a003 100644 --- a/avm/res/api-management/service/api/diagnostics/main.json +++ b/avm/res/api-management/service/api/diagnostics/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15990218139655805007" + "version": "0.29.47.4906", + "templateHash": "15630166564208731013" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", diff --git a/avm/res/api-management/service/api/main.bicep b/avm/res/api-management/service/api/main.bicep index f3cf6a2b3c..6b83aae577 100644 --- a/avm/res/api-management/service/api/main.bicep +++ b/avm/res/api-management/service/api/main.bicep @@ -142,7 +142,7 @@ module policy 'policy/main.bicep' = [ params: { apiManagementServiceName: apiManagementServiceName apiName: api.name - format: contains(policy, 'format') ? policy.format : 'xml' + format: policy.?format ?? 'xml' value: policy.value } } diff --git a/avm/res/api-management/service/api/main.json b/avm/res/api-management/service/api/main.json index 151111afb0..970b83350d 100644 --- a/avm/res/api-management/service/api/main.json +++ b/avm/res/api-management/service/api/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4127687153063244658" + "version": "0.29.47.4906", + "templateHash": "17160750790361326516" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", @@ -270,7 +270,9 @@ "apiName": { "value": "[parameters('name')]" }, - "format": "[if(contains(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), createObject('value', coalesce(parameters('policies'), createArray())[copyIndex()].format), createObject('value', 'xml'))]", + "format": { + "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), 'xml')]" + }, "value": { "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].value]" } @@ -281,8 +283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5034167782203178970" + "version": "0.29.47.4906", + "templateHash": "2474188503939052987" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", @@ -428,8 +430,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15990218139655805007" + "version": "0.29.47.4906", + "templateHash": "15630166564208731013" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", diff --git a/avm/res/api-management/service/api/policy/main.json b/avm/res/api-management/service/api/policy/main.json index 31f20f603c..6defcce4a3 100644 --- a/avm/res/api-management/service/api/policy/main.json +++ b/avm/res/api-management/service/api/policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5034167782203178970" + "version": "0.29.47.4906", + "templateHash": "2474188503939052987" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", diff --git a/avm/res/api-management/service/authorization-server/main.json b/avm/res/api-management/service/authorization-server/main.json index 01f4479ad5..e966a03d7f 100644 --- a/avm/res/api-management/service/authorization-server/main.json +++ b/avm/res/api-management/service/authorization-server/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17095671534556603009" + "version": "0.29.47.4906", + "templateHash": "4256977187793378377" }, "name": "API Management Service Authorization Servers", "description": "This module deploys an API Management Service Authorization Server.", diff --git a/avm/res/api-management/service/backend/main.json b/avm/res/api-management/service/backend/main.json index ce7ff8c555..2a5ea70d52 100644 --- a/avm/res/api-management/service/backend/main.json +++ b/avm/res/api-management/service/backend/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1411324864869146839" + "version": "0.29.47.4906", + "templateHash": "2365531440872951056" }, "name": "API Management Service Backends", "description": "This module deploys an API Management Service Backend.", diff --git a/avm/res/api-management/service/cache/main.json b/avm/res/api-management/service/cache/main.json index cccb18e258..b66a377833 100644 --- a/avm/res/api-management/service/cache/main.json +++ b/avm/res/api-management/service/cache/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8223409608484938303" + "version": "0.29.47.4906", + "templateHash": "3234729148013684780" }, "name": "API Management Service Caches", "description": "This module deploys an API Management Service Cache.", diff --git a/avm/res/api-management/service/identity-provider/README.md b/avm/res/api-management/service/identity-provider/README.md index efbbbae18b..eb33eef785 100644 --- a/avm/res/api-management/service/identity-provider/README.md +++ b/avm/res/api-management/service/identity-provider/README.md @@ -38,6 +38,7 @@ This module deploys an API Management Service Identity Provider. | :-- | :-- | :-- | | [`allowedTenants`](#parameter-allowedtenants) | array | List of Allowed Tenants when configuring Azure Active Directory login. - string. | | [`authority`](#parameter-authority) | string | OpenID Connect discovery endpoint hostname for AAD or AAD B2C. | +| [`clientLibrary`](#parameter-clientlibrary) | string | The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider. | | [`passwordResetPolicyName`](#parameter-passwordresetpolicyname) | string | Password Reset Policy Name. Only applies to AAD B2C Identity Provider. | | [`profileEditingPolicyName`](#parameter-profileeditingpolicyname) | string | Profile Editing Policy Name. Only applies to AAD B2C Identity Provider. | | [`signInPolicyName`](#parameter-signinpolicyname) | string | Signin Policy Name. Only applies to AAD B2C Identity Provider. | @@ -91,6 +92,20 @@ OpenID Connect discovery endpoint hostname for AAD or AAD B2C. - Type: string - Default: `''` +### Parameter: `clientLibrary` + +The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'ADAL' + 'MSAL-2' + ] + ``` + ### Parameter: `passwordResetPolicyName` Password Reset Policy Name. Only applies to AAD B2C Identity Provider. diff --git a/avm/res/api-management/service/identity-provider/main.bicep b/avm/res/api-management/service/identity-provider/main.bicep index 0c6ec2ebcb..8306fe54c9 100644 --- a/avm/res/api-management/service/identity-provider/main.bicep +++ b/avm/res/api-management/service/identity-provider/main.bicep @@ -14,6 +14,13 @@ param authority string = '' @description('Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used.') param clientId string = '' +@description('Optional. The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider.') +@allowed([ + 'ADAL' + 'MSAL-2' +]) +param clientLibrary string? + @description('Conditional. Client secret of the Application in external Identity Provider, used to authenticate login request. Required if identity provider is used.') @secure() param clientSecret string = '' @@ -67,6 +74,7 @@ resource identityProvider 'Microsoft.ApiManagement/service/identityProviders@202 profileEditingPolicyName: isAadB2C ? profileEditingPolicyName : null passwordResetPolicyName: isAadB2C ? passwordResetPolicyName : null clientId: clientId + clientLibrary: clientLibrary clientSecret: clientSecret } } diff --git a/avm/res/api-management/service/identity-provider/main.json b/avm/res/api-management/service/identity-provider/main.json index eedd448e18..f9e6cbe086 100644 --- a/avm/res/api-management/service/identity-provider/main.json +++ b/avm/res/api-management/service/identity-provider/main.json @@ -1,11 +1,12 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "834622836195899034" + "version": "0.29.47.4906", + "templateHash": "12757169124799431378" }, "name": "API Management Service Identity Providers", "description": "This module deploys an API Management Service Identity Provider.", @@ -39,6 +40,17 @@ "description": "Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used." } }, + "clientLibrary": { + "type": "string", + "nullable": true, + "allowedValues": [ + "ADAL", + "MSAL-2" + ], + "metadata": { + "description": "Optional. The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider." + } + }, "clientSecret": { "type": "securestring", "defaultValue": "", @@ -106,8 +118,14 @@ "variables": { "isAadB2C": "[equals(parameters('type'), 'aadB2C')]" }, - "resources": [ - { + "resources": { + "service": { + "existing": true, + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" + }, + "identityProvider": { "type": "Microsoft.ApiManagement/service/identityProviders", "apiVersion": "2022-08-01", "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", @@ -121,10 +139,14 @@ "profileEditingPolicyName": "[if(variables('isAadB2C'), parameters('profileEditingPolicyName'), null())]", "passwordResetPolicyName": "[if(variables('isAadB2C'), parameters('passwordResetPolicyName'), null())]", "clientId": "[parameters('clientId')]", + "clientLibrary": "[parameters('clientLibrary')]", "clientSecret": "[parameters('clientSecret')]" - } + }, + "dependsOn": [ + "service" + ] } - ], + }, "outputs": { "resourceId": { "type": "string", diff --git a/avm/res/api-management/service/loggers/main.json b/avm/res/api-management/service/loggers/main.json index 34d7e48e02..9a6b6378bd 100644 --- a/avm/res/api-management/service/loggers/main.json +++ b/avm/res/api-management/service/loggers/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11123719171758533120" + "version": "0.29.47.4906", + "templateHash": "12986610229102962453" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index e6ecb97e55..2ee1c95c75 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -1,5 +1,5 @@ metadata name = 'API Management Services' -metadata description = 'This module deploys an API Management Service.' +metadata description = 'This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU.' metadata owner = 'Azure/module-maintainers' @description('Optional. Additional datacenter locations of the API Management service. Not supported with V2 SKUs.') @@ -15,7 +15,7 @@ param certificates array = [] @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true -@description('Optional. Custom properties of the API Management service.') +@description('Optional. Custom properties of the API Management service. Not supported if SKU is Consumption.') param customProperties object = { 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'False' 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'False' @@ -75,14 +75,8 @@ param roleAssignments roleAssignmentType ]) param sku string = 'Premium' -@description('Optional. The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0.') -@allowed([ - 0 - 1 - 2 - 3 -]) -param skuCount int = 2 +@description('Conditional. The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/.') +param skuCapacity int = 2 @description('Optional. The full resource ID of a subnet in a virtual network to deploy the API Management service in.') param subnetResourceId string? @@ -101,7 +95,7 @@ param virtualNetworkType string = 'None' @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingType -@description('Optional. A list of availability zones denoting where the resource needs to come from. Not supported with V2 SKUs.') +@description('Optional. A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku.') param zones array = [1, 2] @description('Optional. Necessary to create a new GUID.') @@ -197,6 +191,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.apimanagement-service.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -222,17 +227,17 @@ resource service 'Microsoft.ApiManagement/service@2023-05-01-preview' = { tags: tags sku: { name: sku - capacity: contains(sku, 'V2') ? 1 : contains(sku, 'Consumption') ? 0 : skuCount + capacity: contains(sku, 'Consumption') ? 0 : contains(sku, 'Developer') ? 1 : skuCapacity } - zones: contains(sku, 'V2') ? null : zones + zones: contains(sku, 'Premium') ? zones : [] identity: identity properties: { publisherEmail: publisherEmail publisherName: publisherName notificationSenderEmail: notificationSenderEmail hostnameConfigurations: hostnameConfigurations - additionalLocations: contains(sku, 'V2') ? null : additionalLocations - customProperties: customProperties + additionalLocations: contains(sku, 'Premium') ? additionalLocations : [] + customProperties: contains(sku, 'Consumption') ? null : customProperties certificates: certificates enableClientCertificate: enableClientCertificate ? true : null disableGateway: disableGateway @@ -294,7 +299,7 @@ module service_apiVersionSets 'api-version-set/main.bicep' = [ params: { apiManagementServiceName: service.name name: apiVersionSet.name - properties: contains(apiVersionSet, 'properties') ? apiVersionSet.properties : {} + properties: apiVersionSet.?properties ?? {} } } ] @@ -306,40 +311,20 @@ module service_authorizationServers 'authorization-server/main.bicep' = [ apiManagementServiceName: service.name name: authorizationServer.name authorizationEndpoint: authorizationServer.authorizationEndpoint - authorizationMethods: contains(authorizationServer, 'authorizationMethods') - ? authorizationServer.authorizationMethods - : [ - 'GET' - ] - bearerTokenSendingMethods: contains(authorizationServer, 'bearerTokenSendingMethods') - ? authorizationServer.bearerTokenSendingMethods - : [ - 'authorizationHeader' - ] - clientAuthenticationMethod: contains(authorizationServer, 'clientAuthenticationMethod') - ? authorizationServer.clientAuthenticationMethod - : [ - 'Basic' - ] + authorizationMethods: authorizationServer.?authorizationMethods ?? ['GET'] + bearerTokenSendingMethods: authorizationServer.?bearerTokenSendingMethods ?? ['authorizationHeader'] + clientAuthenticationMethod: authorizationServer.?clientAuthenticationMethod ?? ['Basic'] clientId: authorizationServer.clientId clientSecret: authorizationServer.clientSecret - clientRegistrationEndpoint: contains(authorizationServer, 'clientRegistrationEndpoint') - ? authorizationServer.clientRegistrationEndpoint - : '' - defaultScope: contains(authorizationServer, 'defaultScope') ? authorizationServer.defaultScope : '' + clientRegistrationEndpoint: authorizationServer.?clientRegistrationEndpoint ?? '' + defaultScope: authorizationServer.?defaultScope ?? '' grantTypes: authorizationServer.grantTypes - resourceOwnerPassword: contains(authorizationServer, 'resourceOwnerPassword') - ? authorizationServer.resourceOwnerPassword - : '' - resourceOwnerUsername: contains(authorizationServer, 'resourceOwnerUsername') - ? authorizationServer.resourceOwnerUsername - : '' - serverDescription: contains(authorizationServer, 'serverDescription') ? authorizationServer.serverDescription : '' - supportState: contains(authorizationServer, 'supportState') ? authorizationServer.supportState : false - tokenBodyParameters: contains(authorizationServer, 'tokenBodyParameters') - ? authorizationServer.tokenBodyParameters - : [] - tokenEndpoint: contains(authorizationServer, 'tokenEndpoint') ? authorizationServer.tokenEndpoint : '' + resourceOwnerPassword: authorizationServer.?resourceOwnerPassword ?? '' + resourceOwnerUsername: authorizationServer.?resourceOwnerUsername ?? '' + serverDescription: authorizationServer.?serverDescription ?? '' + supportState: authorizationServer.?supportState ?? false + tokenBodyParameters: authorizationServer.?tokenBodyParameters ?? [] + tokenEndpoint: authorizationServer.?tokenEndpoint ?? '' } } ] @@ -408,20 +393,17 @@ module service_identityProviders 'identity-provider/main.bicep' = [ params: { apiManagementServiceName: service.name name: identityProvider.name - allowedTenants: contains(identityProvider, 'allowedTenants') ? identityProvider.allowedTenants : [] - authority: contains(identityProvider, 'authority') ? identityProvider.authority : '' - clientId: contains(identityProvider, 'clientId') ? identityProvider.clientId : '' - clientSecret: contains(identityProvider, 'clientSecret') ? identityProvider.clientSecret : '' - passwordResetPolicyName: contains(identityProvider, 'passwordResetPolicyName') - ? identityProvider.passwordResetPolicyName - : '' - profileEditingPolicyName: contains(identityProvider, 'profileEditingPolicyName') - ? identityProvider.profileEditingPolicyName - : '' - signInPolicyName: contains(identityProvider, 'signInPolicyName') ? identityProvider.signInPolicyName : '' - signInTenant: contains(identityProvider, 'signInTenant') ? identityProvider.signInTenant : '' - signUpPolicyName: contains(identityProvider, 'signUpPolicyName') ? identityProvider.signUpPolicyName : '' - type: contains(identityProvider, 'type') ? identityProvider.type : 'aad' + allowedTenants: identityProvider.?allowedTenants ?? [] + authority: identityProvider.?authority ?? '' + clientId: identityProvider.?clientId ?? '' + clientLibrary: identityProvider.?clientLibrary ?? '' + clientSecret: identityProvider.?clientSecret ?? '' + passwordResetPolicyName: identityProvider.?passwordResetPolicyName ?? '' + profileEditingPolicyName: identityProvider.?profileEditingPolicyName ?? '' + signInPolicyName: identityProvider.?signInPolicyName ?? '' + signInTenant: identityProvider.?signInTenant ?? '' + signUpPolicyName: identityProvider.?signUpPolicyName ?? '' + type: identityProvider.?type ?? 'aad' } } ] @@ -432,12 +414,15 @@ module service_loggers 'loggers/main.bicep' = [ params: { name: logger.name apiManagementServiceName: service.name - credentials: contains(logger, 'credentials') ? logger.credentials : {} - isBuffered: contains(logger, 'isBuffered') ? logger.isBuffered : true - loggerDescription: contains(logger, 'loggerDescription') ? logger.loggerDescription : '' - loggerType: contains(logger, 'loggerType') ? logger.loggerType : 'azureMonitor' - targetResourceId: contains(logger, 'targetResourceId') ? logger.targetResourceId : '' + credentials: logger.?credentials ?? {} + isBuffered: logger.?isBuffered ?? true + loggerDescription: logger.?loggerDescription ?? '' + loggerType: logger.?loggerType ?? 'azureMonitor' + targetResourceId: logger.?targetResourceId ?? '' } + dependsOn: [ + service_namedValues + ] } ] @@ -447,11 +432,11 @@ module service_namedValues 'named-value/main.bicep' = [ params: { apiManagementServiceName: service.name displayName: namedValue.displayName - keyVault: contains(namedValue, 'keyVault') ? namedValue.keyVault : {} + keyVault: namedValue.?keyVault ?? {} name: namedValue.name tags: namedValue.?tags // Note: these are not resource tags - secret: contains(namedValue, 'secret') ? namedValue.secret : false - value: contains(namedValue, 'value') ? namedValue.value : newGuidValue + secret: namedValue.?secret ?? false + value: namedValue.?value ?? newGuidValue } } ] @@ -473,7 +458,7 @@ module service_policies 'policy/main.bicep' = [ params: { apiManagementServiceName: service.name value: policy.value - format: contains(policy, 'format') ? policy.format : 'xml' + format: policy.?format ?? 'xml' } } ] @@ -483,15 +468,15 @@ module service_products 'product/main.bicep' = [ name: '${uniqueString(deployment().name, location)}-Apim-Product-${index}' params: { apiManagementServiceName: service.name - apis: contains(product, 'apis') ? product.apis : [] - approvalRequired: contains(product, 'approvalRequired') ? product.approvalRequired : false - groups: contains(product, 'groups') ? product.groups : [] + apis: product.?apis ?? [] + approvalRequired: product.?approvalRequired ?? false + groups: product.?groups ?? [] name: product.name - description: contains(product, 'description') ? product.description : '' - state: contains(product, 'state') ? product.state : 'published' - subscriptionRequired: contains(product, 'subscriptionRequired') ? product.subscriptionRequired : false - subscriptionsLimit: contains(product, 'subscriptionsLimit') ? product.subscriptionsLimit : 1 - terms: contains(product, 'terms') ? product.terms : '' + description: product.?description ?? '' + state: product.?state ?? 'published' + subscriptionRequired: product.?subscriptionRequired ?? false + subscriptionsLimit: product.?subscriptionsLimit ?? 1 + terms: product.?terms ?? '' } dependsOn: [ service_apis @@ -556,14 +541,10 @@ resource service_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021- ] resource service_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(service.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(service.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -611,6 +592,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index 23216df2d3..fdb9002616 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11498940478310122046" + "version": "0.29.47.4906", + "templateHash": "17385483787660298242" }, "name": "API Management Services", - "description": "This module deploys an API Management Service.", + "description": "This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -66,6 +66,13 @@ "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": { @@ -290,7 +297,7 @@ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": "False" }, "metadata": { - "description": "Optional. Custom properties of the API Management service." + "description": "Optional. Custom properties of the API Management service. Not supported if SKU is Consumption." } }, "disableGateway": { @@ -388,17 +395,11 @@ "description": "Optional. The pricing tier of this API Management service." } }, - "skuCount": { + "skuCapacity": { "type": "int", "defaultValue": 2, - "allowedValues": [ - 0, - 1, - 2, - 3 - ], "metadata": { - "description": "Optional. The instance size of this API Management service. Not supported with V2 SKUs. If using Consumption, sku should = 0." + "description": "Conditional. The scale units for this API Management service. Required if using Basic, Standard, or Premium skus. For range of capacities for each sku, reference https://azure.microsoft.com/en-us/pricing/details/api-management/." } }, "subnetResourceId": { @@ -440,7 +441,7 @@ 2 ], "metadata": { - "description": "Optional. A list of availability zones denoting where the resource needs to come from. Not supported with V2 SKUs." + "description": "Optional. A list of availability zones denoting where the resource needs to come from. Only supported by Premium sku." } }, "newGuidValue": { @@ -550,6 +551,13 @@ } }, "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)))))]" + } + ], "authorizationServerList": "[if(not(empty(parameters('authorizationServers'))), parameters('authorizationServers').secureList, createArray())]", "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())]", @@ -594,17 +602,17 @@ "tags": "[parameters('tags')]", "sku": { "name": "[parameters('sku')]", - "capacity": "[if(contains(parameters('sku'), 'V2'), 1, if(contains(parameters('sku'), 'Consumption'), 0, parameters('skuCount')))]" + "capacity": "[if(contains(parameters('sku'), 'Consumption'), 0, if(contains(parameters('sku'), 'Developer'), 1, parameters('skuCapacity')))]" }, - "zones": "[if(contains(parameters('sku'), 'V2'), null(), parameters('zones'))]", + "zones": "[if(contains(parameters('sku'), 'Premium'), parameters('zones'), createArray())]", "identity": "[variables('identity')]", "properties": { "publisherEmail": "[parameters('publisherEmail')]", "publisherName": "[parameters('publisherName')]", "notificationSenderEmail": "[parameters('notificationSenderEmail')]", "hostnameConfigurations": "[parameters('hostnameConfigurations')]", - "additionalLocations": "[if(contains(parameters('sku'), 'V2'), null(), parameters('additionalLocations'))]", - "customProperties": "[parameters('customProperties')]", + "additionalLocations": "[if(contains(parameters('sku'), 'Premium'), parameters('additionalLocations'), createArray())]", + "customProperties": "[if(contains(parameters('sku'), 'Consumption'), null(), parameters('customProperties'))]", "certificates": "[parameters('certificates')]", "enableClientCertificate": "[if(parameters('enableClientCertificate'), true(), null())]", "disableGateway": "[parameters('disableGateway')]", @@ -673,20 +681,20 @@ "service_roleAssignments": { "copy": { "name": "service_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ApiManagement/service/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ApiManagement/service', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ApiManagement/service', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "service" @@ -783,8 +791,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4127687153063244658" + "version": "0.29.47.4906", + "templateHash": "17160750790361326516" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", @@ -1048,7 +1056,9 @@ "apiName": { "value": "[parameters('name')]" }, - "format": "[if(contains(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), createObject('value', coalesce(parameters('policies'), createArray())[copyIndex()].format), createObject('value', 'xml'))]", + "format": { + "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'format'), 'xml')]" + }, "value": { "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].value]" } @@ -1059,8 +1069,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5034167782203178970" + "version": "0.29.47.4906", + "templateHash": "2474188503939052987" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", @@ -1206,8 +1216,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15990218139655805007" + "version": "0.29.47.4906", + "templateHash": "15630166564208731013" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -1424,7 +1434,9 @@ "name": { "value": "[parameters('apiVersionSets')[copyIndex()].name]" }, - "properties": "[if(contains(parameters('apiVersionSets')[copyIndex()], 'properties'), createObject('value', parameters('apiVersionSets')[copyIndex()].properties), createObject('value', createObject()))]" + "properties": { + "value": "[coalesce(tryGet(parameters('apiVersionSets')[copyIndex()], 'properties'), createObject())]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1432,8 +1444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2869637630248924190" + "version": "0.29.47.4906", + "templateHash": "17159723717884761443" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set.", @@ -1521,26 +1533,48 @@ "authorizationEndpoint": { "value": "[variables('authorizationServerList')[copyIndex()].authorizationEndpoint]" }, - "authorizationMethods": "[if(contains(variables('authorizationServerList')[copyIndex()], 'authorizationMethods'), createObject('value', variables('authorizationServerList')[copyIndex()].authorizationMethods), createObject('value', createArray('GET')))]", - "bearerTokenSendingMethods": "[if(contains(variables('authorizationServerList')[copyIndex()], 'bearerTokenSendingMethods'), createObject('value', variables('authorizationServerList')[copyIndex()].bearerTokenSendingMethods), createObject('value', createArray('authorizationHeader')))]", - "clientAuthenticationMethod": "[if(contains(variables('authorizationServerList')[copyIndex()], 'clientAuthenticationMethod'), createObject('value', variables('authorizationServerList')[copyIndex()].clientAuthenticationMethod), createObject('value', createArray('Basic')))]", + "authorizationMethods": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'authorizationMethods'), createArray('GET'))]" + }, + "bearerTokenSendingMethods": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'bearerTokenSendingMethods'), createArray('authorizationHeader'))]" + }, + "clientAuthenticationMethod": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'clientAuthenticationMethod'), createArray('Basic'))]" + }, "clientId": { "value": "[variables('authorizationServerList')[copyIndex()].clientId]" }, "clientSecret": { "value": "[variables('authorizationServerList')[copyIndex()].clientSecret]" }, - "clientRegistrationEndpoint": "[if(contains(variables('authorizationServerList')[copyIndex()], 'clientRegistrationEndpoint'), createObject('value', variables('authorizationServerList')[copyIndex()].clientRegistrationEndpoint), createObject('value', ''))]", - "defaultScope": "[if(contains(variables('authorizationServerList')[copyIndex()], 'defaultScope'), createObject('value', variables('authorizationServerList')[copyIndex()].defaultScope), createObject('value', ''))]", + "clientRegistrationEndpoint": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'clientRegistrationEndpoint'), '')]" + }, + "defaultScope": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'defaultScope'), '')]" + }, "grantTypes": { "value": "[variables('authorizationServerList')[copyIndex()].grantTypes]" }, - "resourceOwnerPassword": "[if(contains(variables('authorizationServerList')[copyIndex()], 'resourceOwnerPassword'), createObject('value', variables('authorizationServerList')[copyIndex()].resourceOwnerPassword), createObject('value', ''))]", - "resourceOwnerUsername": "[if(contains(variables('authorizationServerList')[copyIndex()], 'resourceOwnerUsername'), createObject('value', variables('authorizationServerList')[copyIndex()].resourceOwnerUsername), createObject('value', ''))]", - "serverDescription": "[if(contains(variables('authorizationServerList')[copyIndex()], 'serverDescription'), createObject('value', variables('authorizationServerList')[copyIndex()].serverDescription), createObject('value', ''))]", - "supportState": "[if(contains(variables('authorizationServerList')[copyIndex()], 'supportState'), createObject('value', variables('authorizationServerList')[copyIndex()].supportState), createObject('value', false()))]", - "tokenBodyParameters": "[if(contains(variables('authorizationServerList')[copyIndex()], 'tokenBodyParameters'), createObject('value', variables('authorizationServerList')[copyIndex()].tokenBodyParameters), createObject('value', createArray()))]", - "tokenEndpoint": "[if(contains(variables('authorizationServerList')[copyIndex()], 'tokenEndpoint'), createObject('value', variables('authorizationServerList')[copyIndex()].tokenEndpoint), createObject('value', ''))]" + "resourceOwnerPassword": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'resourceOwnerPassword'), '')]" + }, + "resourceOwnerUsername": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'resourceOwnerUsername'), '')]" + }, + "serverDescription": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'serverDescription'), '')]" + }, + "supportState": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'supportState'), false())]" + }, + "tokenBodyParameters": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'tokenBodyParameters'), createArray())]" + }, + "tokenEndpoint": { + "value": "[coalesce(tryGet(variables('authorizationServerList')[copyIndex()], 'tokenEndpoint'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1548,8 +1582,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17095671534556603009" + "version": "0.29.47.4906", + "templateHash": "4256977187793378377" }, "name": "API Management Service Authorization Servers", "description": "This module deploys an API Management Service Authorization Server.", @@ -1791,8 +1825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1411324864869146839" + "version": "0.29.47.4906", + "templateHash": "2365531440872951056" }, "name": "API Management Service Backends", "description": "This module deploys an API Management Service Backend.", @@ -1975,8 +2009,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8223409608484938303" + "version": "0.29.47.4906", + "templateHash": "3234729148013684780" }, "name": "API Management Service Caches", "description": "This module deploys an API Management Service Cache.", @@ -2133,8 +2167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15990218139655805007" + "version": "0.29.47.4906", + "templateHash": "15630166564208731013" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -2322,25 +2356,49 @@ "name": { "value": "[parameters('identityProviders')[copyIndex()].name]" }, - "allowedTenants": "[if(contains(parameters('identityProviders')[copyIndex()], 'allowedTenants'), createObject('value', parameters('identityProviders')[copyIndex()].allowedTenants), createObject('value', createArray()))]", - "authority": "[if(contains(parameters('identityProviders')[copyIndex()], 'authority'), createObject('value', parameters('identityProviders')[copyIndex()].authority), createObject('value', ''))]", - "clientId": "[if(contains(parameters('identityProviders')[copyIndex()], 'clientId'), createObject('value', parameters('identityProviders')[copyIndex()].clientId), createObject('value', ''))]", - "clientSecret": "[if(contains(parameters('identityProviders')[copyIndex()], 'clientSecret'), createObject('value', parameters('identityProviders')[copyIndex()].clientSecret), createObject('value', ''))]", - "passwordResetPolicyName": "[if(contains(parameters('identityProviders')[copyIndex()], 'passwordResetPolicyName'), createObject('value', parameters('identityProviders')[copyIndex()].passwordResetPolicyName), createObject('value', ''))]", - "profileEditingPolicyName": "[if(contains(parameters('identityProviders')[copyIndex()], 'profileEditingPolicyName'), createObject('value', parameters('identityProviders')[copyIndex()].profileEditingPolicyName), createObject('value', ''))]", - "signInPolicyName": "[if(contains(parameters('identityProviders')[copyIndex()], 'signInPolicyName'), createObject('value', parameters('identityProviders')[copyIndex()].signInPolicyName), createObject('value', ''))]", - "signInTenant": "[if(contains(parameters('identityProviders')[copyIndex()], 'signInTenant'), createObject('value', parameters('identityProviders')[copyIndex()].signInTenant), createObject('value', ''))]", - "signUpPolicyName": "[if(contains(parameters('identityProviders')[copyIndex()], 'signUpPolicyName'), createObject('value', parameters('identityProviders')[copyIndex()].signUpPolicyName), createObject('value', ''))]", - "type": "[if(contains(parameters('identityProviders')[copyIndex()], 'type'), createObject('value', parameters('identityProviders')[copyIndex()].type), createObject('value', 'aad'))]" + "allowedTenants": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'allowedTenants'), createArray())]" + }, + "authority": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'authority'), '')]" + }, + "clientId": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientId'), '')]" + }, + "clientLibrary": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientLibrary'), '')]" + }, + "clientSecret": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'clientSecret'), '')]" + }, + "passwordResetPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'passwordResetPolicyName'), '')]" + }, + "profileEditingPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'profileEditingPolicyName'), '')]" + }, + "signInPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInPolicyName'), '')]" + }, + "signInTenant": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signInTenant'), '')]" + }, + "signUpPolicyName": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'signUpPolicyName'), '')]" + }, + "type": { + "value": "[coalesce(tryGet(parameters('identityProviders')[copyIndex()], 'type'), 'aad')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "834622836195899034" + "version": "0.29.47.4906", + "templateHash": "12757169124799431378" }, "name": "API Management Service Identity Providers", "description": "This module deploys an API Management Service Identity Provider.", @@ -2374,6 +2432,17 @@ "description": "Conditional. Client ID of the Application in the external Identity Provider. Required if identity provider is used." } }, + "clientLibrary": { + "type": "string", + "nullable": true, + "allowedValues": [ + "ADAL", + "MSAL-2" + ], + "metadata": { + "description": "Optional. The client library to be used in the developer portal. Only applies to AAD and AAD B2C Identity Provider." + } + }, "clientSecret": { "type": "securestring", "defaultValue": "", @@ -2441,8 +2510,14 @@ "variables": { "isAadB2C": "[equals(parameters('type'), 'aadB2C')]" }, - "resources": [ - { + "resources": { + "service": { + "existing": true, + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('apiManagementServiceName')]" + }, + "identityProvider": { "type": "Microsoft.ApiManagement/service/identityProviders", "apiVersion": "2022-08-01", "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", @@ -2456,10 +2531,14 @@ "profileEditingPolicyName": "[if(variables('isAadB2C'), parameters('profileEditingPolicyName'), null())]", "passwordResetPolicyName": "[if(variables('isAadB2C'), parameters('passwordResetPolicyName'), null())]", "clientId": "[parameters('clientId')]", + "clientLibrary": "[parameters('clientLibrary')]", "clientSecret": "[parameters('clientSecret')]" - } + }, + "dependsOn": [ + "service" + ] } - ], + }, "outputs": { "resourceId": { "type": "string", @@ -2509,11 +2588,21 @@ "apiManagementServiceName": { "value": "[parameters('name')]" }, - "credentials": "[if(contains(parameters('loggers')[copyIndex()], 'credentials'), createObject('value', parameters('loggers')[copyIndex()].credentials), createObject('value', createObject()))]", - "isBuffered": "[if(contains(parameters('loggers')[copyIndex()], 'isBuffered'), createObject('value', parameters('loggers')[copyIndex()].isBuffered), createObject('value', true()))]", - "loggerDescription": "[if(contains(parameters('loggers')[copyIndex()], 'loggerDescription'), createObject('value', parameters('loggers')[copyIndex()].loggerDescription), createObject('value', ''))]", - "loggerType": "[if(contains(parameters('loggers')[copyIndex()], 'loggerType'), createObject('value', parameters('loggers')[copyIndex()].loggerType), createObject('value', 'azureMonitor'))]", - "targetResourceId": "[if(contains(parameters('loggers')[copyIndex()], 'targetResourceId'), createObject('value', parameters('loggers')[copyIndex()].targetResourceId), createObject('value', ''))]" + "credentials": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'credentials'), createObject())]" + }, + "isBuffered": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'isBuffered'), true())]" + }, + "loggerDescription": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'loggerDescription'), '')]" + }, + "loggerType": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'loggerType'), 'azureMonitor')]" + }, + "targetResourceId": { + "value": "[coalesce(tryGet(parameters('loggers')[copyIndex()], 'targetResourceId'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2521,8 +2610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11123719171758533120" + "version": "0.29.47.4906", + "templateHash": "12986610229102962453" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", @@ -2618,7 +2707,8 @@ } }, "dependsOn": [ - "service" + "service", + "service_namedValues" ] }, "service_namedValues": { @@ -2641,15 +2731,21 @@ "displayName": { "value": "[parameters('namedValues')[copyIndex()].displayName]" }, - "keyVault": "[if(contains(parameters('namedValues')[copyIndex()], 'keyVault'), createObject('value', parameters('namedValues')[copyIndex()].keyVault), createObject('value', createObject()))]", + "keyVault": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'keyVault'), createObject())]" + }, "name": { "value": "[parameters('namedValues')[copyIndex()].name]" }, "tags": { "value": "[tryGet(parameters('namedValues')[copyIndex()], 'tags')]" }, - "secret": "[if(contains(parameters('namedValues')[copyIndex()], 'secret'), createObject('value', parameters('namedValues')[copyIndex()].secret), createObject('value', false()))]", - "value": "[if(contains(parameters('namedValues')[copyIndex()], 'value'), createObject('value', parameters('namedValues')[copyIndex()].value), createObject('value', parameters('newGuidValue')))]" + "secret": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'secret'), false())]" + }, + "value": { + "value": "[coalesce(tryGet(parameters('namedValues')[copyIndex()], 'value'), parameters('newGuidValue'))]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2658,8 +2754,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14618669791216532904" + "version": "0.29.47.4906", + "templateHash": "3479776319506170502" }, "name": "API Management Service Named Values", "description": "This module deploys an API Management Service Named Value.", @@ -2799,8 +2895,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "535385624688793699" + "version": "0.29.47.4906", + "templateHash": "10271256088614129674" }, "name": "API Management Service Portal Settings", "description": "This module deploys an API Management Service Portal Setting.", @@ -2888,7 +2984,9 @@ "value": { "value": "[parameters('policies')[copyIndex()].value]" }, - "format": "[if(contains(parameters('policies')[copyIndex()], 'format'), createObject('value', parameters('policies')[copyIndex()].format), createObject('value', 'xml'))]" + "format": { + "value": "[coalesce(tryGet(parameters('policies')[copyIndex()], 'format'), 'xml')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2896,8 +2994,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5324506884411715219" + "version": "0.29.47.4906", + "templateHash": "11443463088593763324" }, "name": "API Management Service Policies", "description": "This module deploys an API Management Service Policy.", @@ -2994,17 +3092,33 @@ "apiManagementServiceName": { "value": "[parameters('name')]" }, - "apis": "[if(contains(parameters('products')[copyIndex()], 'apis'), createObject('value', parameters('products')[copyIndex()].apis), createObject('value', createArray()))]", - "approvalRequired": "[if(contains(parameters('products')[copyIndex()], 'approvalRequired'), createObject('value', parameters('products')[copyIndex()].approvalRequired), createObject('value', false()))]", - "groups": "[if(contains(parameters('products')[copyIndex()], 'groups'), createObject('value', parameters('products')[copyIndex()].groups), createObject('value', createArray()))]", + "apis": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'apis'), createArray())]" + }, + "approvalRequired": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'approvalRequired'), false())]" + }, + "groups": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'groups'), createArray())]" + }, "name": { "value": "[parameters('products')[copyIndex()].name]" }, - "description": "[if(contains(parameters('products')[copyIndex()], 'description'), createObject('value', parameters('products')[copyIndex()].description), createObject('value', ''))]", - "state": "[if(contains(parameters('products')[copyIndex()], 'state'), createObject('value', parameters('products')[copyIndex()].state), createObject('value', 'published'))]", - "subscriptionRequired": "[if(contains(parameters('products')[copyIndex()], 'subscriptionRequired'), createObject('value', parameters('products')[copyIndex()].subscriptionRequired), createObject('value', false()))]", - "subscriptionsLimit": "[if(contains(parameters('products')[copyIndex()], 'subscriptionsLimit'), createObject('value', parameters('products')[copyIndex()].subscriptionsLimit), createObject('value', 1))]", - "terms": "[if(contains(parameters('products')[copyIndex()], 'terms'), createObject('value', parameters('products')[copyIndex()].terms), createObject('value', ''))]" + "description": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'description'), '')]" + }, + "state": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'state'), 'published')]" + }, + "subscriptionRequired": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionRequired'), false())]" + }, + "subscriptionsLimit": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'subscriptionsLimit'), 1)]" + }, + "terms": { + "value": "[coalesce(tryGet(parameters('products')[copyIndex()], 'terms'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -3012,8 +3126,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13125454306061578309" + "version": "0.29.47.4906", + "templateHash": "6230115773857876317" }, "name": "API Management Service Products", "description": "This module deploys an API Management Service Product.", @@ -3134,8 +3248,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4018261656720912671" + "version": "0.29.47.4906", + "templateHash": "1052981479169082206" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", @@ -3224,8 +3338,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6078964374640716249" + "version": "0.29.47.4906", + "templateHash": "5748451278124986706" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", @@ -3381,8 +3495,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4461001441551159036" + "version": "0.29.47.4906", + "templateHash": "9499976066778278010" }, "name": "API Management Service Subscriptions", "description": "This module deploys an API Management Service Subscription.", diff --git a/avm/res/api-management/service/named-value/main.json b/avm/res/api-management/service/named-value/main.json index fdc0c0b970..4be9cba518 100644 --- a/avm/res/api-management/service/named-value/main.json +++ b/avm/res/api-management/service/named-value/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14618669791216532904" + "version": "0.29.47.4906", + "templateHash": "3479776319506170502" }, "name": "API Management Service Named Values", "description": "This module deploys an API Management Service Named Value.", diff --git a/avm/res/api-management/service/policy/main.json b/avm/res/api-management/service/policy/main.json index 463c1a7203..83d9434240 100644 --- a/avm/res/api-management/service/policy/main.json +++ b/avm/res/api-management/service/policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5324506884411715219" + "version": "0.29.47.4906", + "templateHash": "11443463088593763324" }, "name": "API Management Service Policies", "description": "This module deploys an API Management Service Policy.", diff --git a/avm/res/api-management/service/portalsetting/main.json b/avm/res/api-management/service/portalsetting/main.json index 7126cf7ed1..779c574120 100644 --- a/avm/res/api-management/service/portalsetting/main.json +++ b/avm/res/api-management/service/portalsetting/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "535385624688793699" + "version": "0.29.47.4906", + "templateHash": "10271256088614129674" }, "name": "API Management Service Portal Settings", "description": "This module deploys an API Management Service Portal Setting.", diff --git a/avm/res/api-management/service/product/api/main.json b/avm/res/api-management/service/product/api/main.json index 5eb9fa4987..4042b9bf61 100644 --- a/avm/res/api-management/service/product/api/main.json +++ b/avm/res/api-management/service/product/api/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4018261656720912671" + "version": "0.29.47.4906", + "templateHash": "1052981479169082206" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", diff --git a/avm/res/api-management/service/product/group/main.json b/avm/res/api-management/service/product/group/main.json index df0b793b4a..4ac13f0dac 100644 --- a/avm/res/api-management/service/product/group/main.json +++ b/avm/res/api-management/service/product/group/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6078964374640716249" + "version": "0.29.47.4906", + "templateHash": "5748451278124986706" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", diff --git a/avm/res/api-management/service/product/main.json b/avm/res/api-management/service/product/main.json index 05b02c725b..73dd3977b6 100644 --- a/avm/res/api-management/service/product/main.json +++ b/avm/res/api-management/service/product/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13125454306061578309" + "version": "0.29.47.4906", + "templateHash": "6230115773857876317" }, "name": "API Management Service Products", "description": "This module deploys an API Management Service Product.", @@ -126,8 +126,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4018261656720912671" + "version": "0.29.47.4906", + "templateHash": "1052981479169082206" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", @@ -216,8 +216,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6078964374640716249" + "version": "0.29.47.4906", + "templateHash": "5748451278124986706" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", diff --git a/avm/res/api-management/service/subscription/main.json b/avm/res/api-management/service/subscription/main.json index 5a7676a0e6..5510d60858 100644 --- a/avm/res/api-management/service/subscription/main.json +++ b/avm/res/api-management/service/subscription/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4461001441551159036" + "version": "0.29.47.4906", + "templateHash": "9499976066778278010" }, "name": "API Management Service Subscriptions", "description": "This module deploys an API Management Service Subscription.", diff --git a/avm/res/api-management/service/tests/e2e/consumptionSku/main.test.bicep b/avm/res/api-management/service/tests/e2e/consumptionSku/main.test.bicep new file mode 100644 index 0000000000..98c77a6933 --- /dev/null +++ b/avm/res/api-management/service/tests/e2e/consumptionSku/main.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +metadata name = 'Deploying a Consumption SKU' +metadata description = 'This instance deploys the module using a Consumption SKU.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-apimanagement.service-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apiscon' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '${namePrefix}-az-amorg-x-001' + sku: 'Consumption' + } + } +] diff --git a/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep b/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep index 4b757bf9b6..d1dd29a05c 100644 --- a/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/defaults/main.test.bicep @@ -36,13 +36,15 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' - publisherName: '${namePrefix}-az-amorg-x-001' +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '${namePrefix}-az-amorg-x-001' + } } -}] +] diff --git a/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep b/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep new file mode 100644 index 0000000000..419585af28 --- /dev/null +++ b/avm/res/api-management/service/tests/e2e/developerSku/main.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +metadata name = 'Deploying a Developer SKU' +metadata description = 'This instance deploys the module using a Developer SKU.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-apimanagement.service-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apisdev' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: '${namePrefix}-az-amorg-x-001' + sku: 'Developer' + } + } +] diff --git a/avm/res/api-management/service/tests/e2e/max/main.test.bicep b/avm/res/api-management/service/tests/e2e/max/main.test.bicep index 1f321c8cd7..2989495645 100644 --- a/avm/res/api-management/service/tests/e2e/max/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/max/main.test.bicep @@ -171,6 +171,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'aad' clientId: 'apimClientid' + clientLibrary: 'MSAL-2' clientSecret: 'apimSlientSecret' authority: split(environment().authentication.loginEndpoint, '/')[2] signinTenant: 'mytenant.onmicrosoft.com' @@ -245,11 +246,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '6352c3e3-ac6b-43d5-ac43-1077ff373721' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/api-management/service/tests/e2e/v2sku/main.test.bicep b/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep similarity index 93% rename from avm/res/api-management/service/tests/e2e/v2sku/main.test.bicep rename to avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep index 49fa338dc6..62151c9a05 100644 --- a/avm/res/api-management/service/tests/e2e/v2sku/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/v2Sku/main.test.bicep @@ -1,7 +1,7 @@ targetScope = 'subscription' -metadata name = 'Test deploying apim v2 sku' -metadata description = 'This instance deploys the module using a v2 SKU with the minimum set of required parameters.' +metadata name = 'Deploying an APIM v2 sku' +metadata description = 'This instance deploys the module using a v2 SKU.' // ========== // // Parameters // diff --git a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep index 35fac0d255..ccf1f295b4 100644 --- a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep @@ -161,6 +161,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'aad' clientId: 'apimClientid' + clientLibrary: 'MSAL-2' clientSecret: customSecret authority: split(environment().authentication.loginEndpoint, '/')[2] signinTenant: 'mytenant.onmicrosoft.com' diff --git a/avm/res/api-management/service/version.json b/avm/res/api-management/service/version.json index 9481fea58e..3f863a2bec 100644 --- a/avm/res/api-management/service/version.json +++ b/avm/res/api-management/service/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/app-configuration/configuration-store/README.md b/avm/res/app-configuration/configuration-store/README.md index 183e5ecd65..98376c6b95 100644 --- a/avm/res/app-configuration/configuration-store/README.md +++ b/avm/res/app-configuration/configuration-store/README.md @@ -21,8 +21,8 @@ This module deploys an App Configuration Store. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -247,9 +247,20 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto name: 'keyName' roleAssignments: [ { + name: '56e2c190-b31e-4518-84de-170b8a5c1b24' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] value: 'valueName' @@ -276,11 +287,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto ] roleAssignments: [ { + name: '695044c2-3f1f-4843-970a-bed584b95a9a' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -350,9 +363,20 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto "name": "keyName", "roleAssignments": [ { + "name": "56e2c190-b31e-4518-84de-170b8a5c1b24", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "value": "valueName" @@ -389,11 +413,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto "roleAssignments": { "value": [ { + "name": "695044c2-3f1f-4843-970a-bed584b95a9a", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -443,9 +469,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto location: '' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -454,9 +484,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -494,9 +528,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -505,9 +543,13 @@ module configurationStore 'br/public:avm/res/app-configuration/configuration-sto } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -999,8 +1041,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1184,19 +1225,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1234,6 +1320,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1284,6 +1371,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1358,6 +1452,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1408,6 +1503,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1463,6 +1565,7 @@ Tags of the resource. | `endpoint` | string | The endpoint of the app configuration. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the app configuration. | +| `privateEndpoints` | array | The private endpoints of the app configuration. | | `resourceGroupName` | string | The resource group the app configuration store was deployed into. | | `resourceId` | string | The resource ID of the app configuration. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1473,7 +1576,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/app-configuration/configuration-store/key-value/main.json b/avm/res/app-configuration/configuration-store/key-value/main.json index e214bc0fa3..58dc82fcea 100644 --- a/avm/res/app-configuration/configuration-store/key-value/main.json +++ b/avm/res/app-configuration/configuration-store/key-value/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4987655092014889247" + "version": "0.29.47.4906", + "templateHash": "11258786733278014615" }, "name": "App Configuration Stores Key Values", "description": "This module deploys an App Configuration Store Key Value.", diff --git a/avm/res/app-configuration/configuration-store/main.bicep b/avm/res/app-configuration/configuration-store/main.bicep index f371cba6e2..ae336c1257 100644 --- a/avm/res/app-configuration/configuration-store/main.bicep +++ b/avm/res/app-configuration/configuration-store/main.bicep @@ -115,6 +115,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.appconfiguration-configurationstore.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -249,14 +260,14 @@ resource configurationStore_diagnosticSettings 'Microsoft.Insights/diagnosticSet ] resource configurationStore_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(configurationStore.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + configurationStore.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -269,7 +280,7 @@ resource configurationStore_roleAssignments 'Microsoft.Authorization/roleAssignm ] @batchSize(1) -module configurationStore_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module configurationStore_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-configurationStore-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -310,8 +321,7 @@ module configurationStore_privateEndpoints 'br/public:avm/res/network/private-en 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -340,6 +350,17 @@ output location string = configurationStore.location @description('The endpoint of the app configuration.') output endpoint string = configurationStore.properties.endpoint +@description('The private endpoints of the app configuration.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: configurationStore_privateEndpoints[i].outputs.name + resourceId: configurationStore_privateEndpoints[i].outputs.resourceId + groupId: configurationStore_privateEndpoints[i].outputs.groupId + customDnsConfig: configurationStore_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: configurationStore_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -361,6 +382,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -399,11 +423,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/app-configuration/configuration-store/main.json b/avm/res/app-configuration/configuration-store/main.json index a7f408ea2b..969b9aedd2 100644 --- a/avm/res/app-configuration/configuration-store/main.json +++ b/avm/res/app-configuration/configuration-store/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16568444774121110101" + "version": "0.29.47.4906", + "templateHash": "11272722521128633240" }, "name": "App Configuration Stores", "description": "This module deploys an App Configuration Store.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -599,6 +629,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -745,20 +782,20 @@ "configurationStore_roleAssignments": { "copy": { "name": "configurationStore_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "configurationStore" @@ -801,8 +838,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1895784594394176994" + "version": "0.29.47.4906", + "templateHash": "11258786733278014615" }, "name": "App Configuration Stores Key Values", "description": "This module deploys an App Configuration Store Key Value.", @@ -922,8 +959,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15490063653423076927" + "version": "0.29.47.4906", + "templateHash": "12501157551760596905" }, "name": "App Configuration Replicas", "description": "This module deploys an App Configuration Replica.", @@ -1020,11 +1057,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1052,19 +1086,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1168,13 +1230,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1208,8 +1270,11 @@ "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." + "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": { @@ -1249,8 +1314,11 @@ "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." + "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": { @@ -1298,6 +1366,29 @@ } }, "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": { @@ -1333,18 +1424,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1400,6 +1484,13 @@ } }, "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')]", @@ -1417,8 +1508,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1436,7 +1527,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1477,27 +1568,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1508,28 +1599,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1537,12 +1652,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1556,27 +1674,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1634,14 +1761,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1693,6 +1834,22 @@ "description": "The endpoint of the app configuration." }, "value": "[reference('configurationStore').endpoint]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the app configuration." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/app-configuration/configuration-store/replicas/main.json b/avm/res/app-configuration/configuration-store/replicas/main.json index 62607f0036..220da65163 100644 --- a/avm/res/app-configuration/configuration-store/replicas/main.json +++ b/avm/res/app-configuration/configuration-store/replicas/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3867721314598368740" + "version": "0.29.47.4906", + "templateHash": "12501157551760596905" }, "name": "App Configuration Replicas", "description": "This module deploys an App Configuration Replica.", diff --git a/avm/res/app-configuration/configuration-store/tests/e2e/max/main.test.bicep b/avm/res/app-configuration/configuration-store/tests/e2e/max/main.test.bicep index be0d37803b..77b1a83195 100644 --- a/avm/res/app-configuration/configuration-store/tests/e2e/max/main.test.bicep +++ b/avm/res/app-configuration/configuration-store/tests/e2e/max/main.test.bicep @@ -93,7 +93,21 @@ module testDeployment '../../../main.bicep' = [ name: 'keyName' roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: '56e2c190-b31e-4518-84de-170b8a5c1b24' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -111,11 +125,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '695044c2-3f1f-4843-970a-bed584b95a9a' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/app-configuration/configuration-store/tests/e2e/pe/main.test.bicep b/avm/res/app-configuration/configuration-store/tests/e2e/pe/main.test.bicep index b3221b8795..dd8cfe1384 100644 --- a/avm/res/app-configuration/configuration-store/tests/e2e/pe/main.test.bicep +++ b/avm/res/app-configuration/configuration-store/tests/e2e/pe/main.test.bicep @@ -53,12 +53,16 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' location: resourceLocation createMode: 'Default' - enablePurgeProtection: false //Only for Testing purposes. Waf Aligned is true + enablePurgeProtection: false // Only for Testing purposes. Waf Aligned is true privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -68,9 +72,13 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] softDeleteRetentionInDays: 1 diff --git a/avm/res/app-configuration/configuration-store/version.json b/avm/res/app-configuration/configuration-store/version.json index 9481fea58e..3f863a2bec 100644 --- a/avm/res/app-configuration/configuration-store/version.json +++ b/avm/res/app-configuration/configuration-store/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md index e7035c0b37..5ad671261d 100644 --- a/avm/res/app/container-app/README.md +++ b/avm/res/app/container-app/README.md @@ -15,7 +15,7 @@ This module deploys a Container App. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.App/containerApps` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-05-01/containerApps) | +| `Microsoft.App/containerApps` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-03-01/containerApps) | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | @@ -28,8 +28,10 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/app/container-app:`. - [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [WAF-aligned](#example-3-waf-aligned) +- [Without ingress enabled](#example-2-without-ingress-enabled) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [VNet integrated container app deployment](#example-4-vnet-integrated-container-app-deployment) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -55,7 +57,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } } ] - environmentId: '' + environmentResourceId: '' name: 'acamin001' // Non-required parameters location: '' @@ -88,8 +90,8 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } ] }, - "environmentId": { - "value": "" + "environmentResourceId": { + "value": "" }, "name": { "value": "acamin001" @@ -105,7 +107,85 @@ module containerApp 'br/public:avm/res/app/container-app:' = {

-### Example 2: _Using large parameter set_ +### Example 2: _Without ingress enabled_ + +This instance deploys the module with ingress traffic completely disabled. + + +

+ +via Bicep module + +```bicep +module containerApp 'br/public:avm/res/app/container-app:' = { + name: 'containerAppDeployment' + params: { + // Required parameters + containers: [ + { + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: 'simple-hello-world-container' + resources: { + cpu: '' + memory: '0.5Gi' + } + } + ] + environmentResourceId: '' + name: 'acapriv001' + // Non-required parameters + disableIngress: true + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containers": { + "value": [ + { + "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", + "name": "simple-hello-world-container", + "resources": { + "cpu": "", + "memory": "0.5Gi" + } + } + ] + }, + "environmentResourceId": { + "value": "" + }, + "name": { + "value": "acapriv001" + }, + // Non-required parameters + "disableIngress": { + "value": true + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -121,6 +201,16 @@ module containerApp 'br/public:avm/res/app/container-app:' = { // Required parameters containers: [ { + env: [ + { + name: 'ContainerAppStoredSecretName' + secretRef: 'containerappstoredsecret' + } + { + name: 'ContainerAppKeyVaultStoredSecretName' + secretRef: 'keyvaultstoredsecret' + } + ] image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' name: 'simple-hello-world-container' probes: [ @@ -146,7 +236,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } } ] - environmentId: '' + environmentResourceId: '' name: 'acamax001' // Non-required parameters location: '' @@ -161,11 +251,13 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } roleAssignments: [ { + name: 'e9bac1ee-aebe-4513-9337-49e87a7be05e' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -179,9 +271,14 @@ module containerApp 'br/public:avm/res/app/container-app:' = { secrets: { secureList: [ { - name: 'customtest' + name: 'containerappstoredsecret' value: '' } + { + identity: '' + keyVaultUrl: '' + name: 'keyvaultstoredsecret' + } ] } tags: { @@ -208,6 +305,16 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "containers": { "value": [ { + "env": [ + { + "name": "ContainerAppStoredSecretName", + "secretRef": "containerappstoredsecret" + }, + { + "name": "ContainerAppKeyVaultStoredSecretName", + "secretRef": "keyvaultstoredsecret" + } + ], "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", "name": "simple-hello-world-container", "probes": [ @@ -234,8 +341,8 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } ] }, - "environmentId": { - "value": "" + "environmentResourceId": { + "value": "" }, "name": { "value": "acamax001" @@ -260,11 +367,13 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "roleAssignments": { "value": [ { + "name": "e9bac1ee-aebe-4513-9337-49e87a7be05e", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -280,8 +389,13 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "value": { "secureList": [ { - "name": "customtest", + "name": "containerappstoredsecret", "value": "" + }, + { + "identity": "", + "keyVaultUrl": "", + "name": "keyvaultstoredsecret" } ] } @@ -299,7 +413,113 @@ module containerApp 'br/public:avm/res/app/container-app:' = {

-### Example 3: _WAF-aligned_ +### Example 4: _VNet integrated container app deployment_ + +This instance deploys the container app in a managed environment with a virtual network using TCP ingress. + + +

+ +via Bicep module + +```bicep +module containerApp 'br/public:avm/res/app/container-app:' = { + name: 'containerAppDeployment' + params: { + // Required parameters + containers: [ + { + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: 'simple-hello-world-container' + resources: { + cpu: '' + memory: '0.5Gi' + } + } + ] + environmentResourceId: '' + name: 'acavnet001' + // Non-required parameters + additionalPortMappings: [ + { + exposedPort: 8080 + external: false + targetPort: 8080 + } + ] + ingressAllowInsecure: false + ingressExternal: false + ingressTargetPort: 80 + ingressTransport: 'tcp' + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "containers": { + "value": [ + { + "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", + "name": "simple-hello-world-container", + "resources": { + "cpu": "", + "memory": "0.5Gi" + } + } + ] + }, + "environmentResourceId": { + "value": "" + }, + "name": { + "value": "acavnet001" + }, + // Non-required parameters + "additionalPortMappings": { + "value": [ + { + "exposedPort": 8080, + "external": false, + "targetPort": 8080 + } + ] + }, + "ingressAllowInsecure": { + "value": false + }, + "ingressExternal": { + "value": false + }, + "ingressTargetPort": { + "value": 80 + }, + "ingressTransport": { + "value": "tcp" + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -340,7 +560,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } } ] - environmentId: '' + environmentResourceId: '' name: 'acawaf001' // Non-required parameters ingressAllowInsecure: false @@ -405,8 +625,8 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } ] }, - "environmentId": { - "value": "" + "environmentResourceId": { + "value": "" }, "name": { "value": "acawaf001" @@ -455,7 +675,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`containers`](#parameter-containers) | array | List of container definitions for the Container App. | -| [`environmentId`](#parameter-environmentid) | string | Resource ID of environment. | +| [`environmentResourceId`](#parameter-environmentresourceid) | string | Resource ID of environment. | | [`name`](#parameter-name) | string | Name of the Container App. | **Optional parameters** @@ -463,10 +683,15 @@ module containerApp 'br/public:avm/res/app/container-app:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`activeRevisionsMode`](#parameter-activerevisionsmode) | string | Controls how active revisions are handled for the Container app. | +| [`additionalPortMappings`](#parameter-additionalportmappings) | array | Settings to expose additional ports on container app. | +| [`clientCertificateMode`](#parameter-clientcertificatemode) | string | Client certificate mode for mTLS. | +| [`corsPolicy`](#parameter-corspolicy) | object | Object userd to configure CORS policy. | | [`customDomains`](#parameter-customdomains) | array | Custom domain bindings for Container App hostnames. | | [`dapr`](#parameter-dapr) | object | Dapr configuration for the Container App. | +| [`disableIngress`](#parameter-disableingress) | bool | Bool to disable all ingress traffic for the container app. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`exposedPort`](#parameter-exposedport) | int | Exposed Port in containers for TCP traffic from ingress. | +| [`includeAddOns`](#parameter-includeaddons) | bool | Toggle to include the service configuration. | | [`ingressAllowInsecure`](#parameter-ingressallowinsecure) | bool | Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections. | | [`ingressExternal`](#parameter-ingressexternal) | bool | Bool indicating if the App exposes an external HTTP endpoint. | | [`ingressTargetPort`](#parameter-ingresstargetport) | int | Target Port in containers for traffic from ingress. | @@ -484,6 +709,8 @@ module containerApp 'br/public:avm/res/app/container-app:' = { | [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. Defaults to 3 if not set. | | [`scaleRules`](#parameter-scalerules) | array | Scaling rules. | | [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. | +| [`service`](#parameter-service) | object | Dev ContainerApp service type. | +| [`serviceBinds`](#parameter-servicebinds) | array | List of container app services bound to the app. | | [`stickySessionsAffinity`](#parameter-stickysessionsaffinity) | string | Bool indicating if the Container App should enable session affinity. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`trafficLabel`](#parameter-trafficlabel) | string | Associates a traffic label with a revision. Label name should be consist of lower case alphanumeric characters or dashes. | @@ -831,7 +1058,7 @@ Path within the volume from which the container's volume should be mounted. Defa - Required: No - Type: string -### Parameter: `environmentId` +### Parameter: `environmentResourceId` Resource ID of environment. @@ -860,6 +1087,123 @@ Controls how active revisions are handled for the Container app. ] ``` +### Parameter: `additionalPortMappings` + +Settings to expose additional ports on container app. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`external`](#parameter-additionalportmappingsexternal) | bool | Specifies whether the app port is accessible outside of the environment. | +| [`targetPort`](#parameter-additionalportmappingstargetport) | int | Specifies the port the container listens on. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`exposedPort`](#parameter-additionalportmappingsexposedport) | int | Specifies the exposed port for the target port. If not specified, it defaults to target port. | + +### Parameter: `additionalPortMappings.external` + +Specifies whether the app port is accessible outside of the environment. + +- Required: Yes +- Type: bool + +### Parameter: `additionalPortMappings.targetPort` + +Specifies the port the container listens on. + +- Required: Yes +- Type: int + +### Parameter: `additionalPortMappings.exposedPort` + +Specifies the exposed port for the target port. If not specified, it defaults to target port. + +- Required: No +- Type: int + +### Parameter: `clientCertificateMode` + +Client certificate mode for mTLS. + +- Required: No +- Type: string +- Default: `'ignore'` +- Allowed: + ```Bicep + [ + 'accept' + 'ignore' + 'require' + ] + ``` + +### Parameter: `corsPolicy` + +Object userd to configure CORS policy. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowCredentials`](#parameter-corspolicyallowcredentials) | bool | Switch to determine whether the resource allows credentials. | +| [`allowedHeaders`](#parameter-corspolicyallowedheaders) | array | Specifies the content for the access-control-allow-headers header. | +| [`allowedMethods`](#parameter-corspolicyallowedmethods) | array | Specifies the content for the access-control-allow-methods header. | +| [`allowedOrigins`](#parameter-corspolicyallowedorigins) | array | Specifies the content for the access-control-allow-origins header. | +| [`exposeHeaders`](#parameter-corspolicyexposeheaders) | array | Specifies the content for the access-control-expose-headers header. | +| [`maxAge`](#parameter-corspolicymaxage) | int | Specifies the content for the access-control-max-age header. | + +### Parameter: `corsPolicy.allowCredentials` + +Switch to determine whether the resource allows credentials. + +- Required: No +- Type: bool + +### Parameter: `corsPolicy.allowedHeaders` + +Specifies the content for the access-control-allow-headers header. + +- Required: No +- Type: array + +### Parameter: `corsPolicy.allowedMethods` + +Specifies the content for the access-control-allow-methods header. + +- Required: No +- Type: array + +### Parameter: `corsPolicy.allowedOrigins` + +Specifies the content for the access-control-allow-origins header. + +- Required: No +- Type: array + +### Parameter: `corsPolicy.exposeHeaders` + +Specifies the content for the access-control-expose-headers header. + +- Required: No +- Type: array + +### Parameter: `corsPolicy.maxAge` + +Specifies the content for the access-control-max-age header. + +- Required: No +- Type: int + ### Parameter: `customDomains` Custom domain bindings for Container App hostnames. @@ -876,6 +1220,14 @@ Dapr configuration for the Container App. - Type: object - Default: `{}` +### Parameter: `disableIngress` + +Bool to disable all ingress traffic for the container app. + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. @@ -892,6 +1244,14 @@ Exposed Port in containers for TCP traffic from ingress. - Type: int - Default: `0` +### Parameter: `includeAddOns` + +Toggle to include the service configuration. + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `ingressAllowInsecure` Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections. @@ -1067,6 +1427,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1117,6 +1478,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1166,6 +1534,42 @@ The secrets of the Container App. - Type: secureObject - Default: `{}` +### Parameter: `service` + +Dev ContainerApp service type. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `serviceBinds` + +List of container app services bound to the app. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-servicebindsname) | string | The name of the service. | +| [`serviceId`](#parameter-servicebindsserviceid) | string | The service ID. | + +### Parameter: `serviceBinds.name` + +The name of the service. + +- Required: Yes +- Type: string + +### Parameter: `serviceBinds.serviceId` + +The service ID. + +- Required: Yes +- Type: string + ### Parameter: `stickySessionsAffinity` Bool indicating if the Container App should enable session affinity. diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep index 118dd16463..e1a21c434c 100644 --- a/avm/res/app/container-app/main.bicep +++ b/avm/res/app/container-app/main.bicep @@ -8,9 +8,23 @@ param name string @description('Optional. Location for all Resources.') param location string = resourceGroup().location +@description('Optional. Bool to disable all ingress traffic for the container app.') +param disableIngress bool = false + @description('Optional. Bool indicating if the App exposes an external HTTP endpoint.') param ingressExternal bool = true +@allowed([ + 'accept' + 'ignore' + 'require' +]) +@description('Optional. Client certificate mode for mTLS.') +param clientCertificateMode string = 'ignore' + +@description('Optional. Object userd to configure CORS policy.') +param corsPolicy corsPolicyType + @allowed([ 'none' 'sticky' @@ -27,6 +41,15 @@ param stickySessionsAffinity string = 'none' @description('Optional. Ingress transport protocol.') param ingressTransport string = 'auto' +@description('Optional. Dev ContainerApp service type.') +param service object = {} + +@description('Optional. Toggle to include the service configuration.') +param includeAddOns bool = false + +@description('Optional. Settings to expose additional ports on container app.') +param additionalPortMappings ingressPortMapping[]? + @description('Optional. Bool indicating if HTTP connections to is allowed. If set to false HTTP connections are automatically redirected to HTTPS connections.') param ingressAllowInsecure bool = true @@ -42,6 +65,9 @@ param scaleMinReplicas int = 3 @description('Optional. Scaling rules.') param scaleRules array = [] +@description('Optional. List of container app services bound to the app.') +param serviceBinds serviceBind[]? + @allowed([ 'Multiple' 'Single' @@ -50,7 +76,7 @@ param scaleRules array = [] param activeRevisionsMode string = 'Single' @description('Required. Resource ID of environment.') -param environmentId string +param environmentResourceId string @description('Optional. The lock settings of the service.') param lock lockType @@ -151,6 +177,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.app-containerapp.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -170,36 +207,53 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource containerApp 'Microsoft.App/containerApps@2023-05-01' = { +resource containerApp 'Microsoft.App/containerApps@2024-03-01' = { name: name tags: tags location: location identity: identity properties: { - environmentId: environmentId + environmentId: environmentResourceId configuration: { activeRevisionsMode: activeRevisionsMode dapr: !empty(dapr) ? dapr : null - ingress: { - allowInsecure: ingressAllowInsecure - customDomains: !empty(customDomains) ? customDomains : null - exposedPort: exposedPort - external: ingressExternal - ipSecurityRestrictions: !empty(ipSecurityRestrictions) ? ipSecurityRestrictions : null - targetPort: ingressTargetPort - stickySessions: { - affinity: stickySessionsAffinity - } - traffic: [ - { - label: trafficLabel - latestRevision: trafficLatestRevision - revisionName: trafficRevisionName - weight: trafficWeight + ingress: disableIngress + ? null + : { + additionalPortMappings: additionalPortMappings + allowInsecure: ingressTransport != 'tcp' ? ingressAllowInsecure : false + customDomains: !empty(customDomains) ? customDomains : null + corsPolicy: corsPolicy != null && ingressTransport != 'tcp' + ? { + allowCredentials: corsPolicy.?allowCredentials ?? false + allowedHeaders: corsPolicy.?allowedHeaders ?? [] + allowedMethods: corsPolicy.?allowedMethods ?? [] + allowedOrigins: corsPolicy.?allowedOrigins ?? [] + exposeHeaders: corsPolicy.?exposeHeaders ?? [] + maxAge: corsPolicy.?maxAge + } + : null + clientCertificateMode: ingressTransport != 'tcp' ? clientCertificateMode : null + exposedPort: exposedPort + external: ingressExternal + ipSecurityRestrictions: !empty(ipSecurityRestrictions) ? ipSecurityRestrictions : null + targetPort: ingressTargetPort + stickySessions: { + affinity: stickySessionsAffinity + } + traffic: ingressTransport != 'tcp' + ? [ + { + label: trafficLabel + latestRevision: trafficLatestRevision + revisionName: trafficRevisionName + weight: trafficWeight + } + ] + : null + transport: ingressTransport } - ] - transport: ingressTransport - } + service: (includeAddOns && !empty(service)) ? service : null maxInactiveRevisions: maxInactiveRevisions registries: !empty(registries) ? registries : null secrets: secretList @@ -213,6 +267,7 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = { minReplicas: scaleMinReplicas rules: !empty(scaleRules) ? scaleRules : null } + serviceBinds: (includeAddOns && !empty(serviceBinds)) ? serviceBinds : null volumes: !empty(volumes) ? volumes : null } workloadProfileName: workloadProfileName @@ -231,14 +286,10 @@ resource containerApp_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!emp } resource containerApp_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(containerApp.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(containerApp.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -254,7 +305,7 @@ resource containerApp_roleAssignments 'Microsoft.Authorization/roleAssignments@2 output resourceId string = containerApp.id @description('The configuration of ingress fqdn.') -output fqdn string = containerApp.properties.configuration.ingress.fqdn +output fqdn string = disableIngress ? 'IngressDisabled' : containerApp.properties.configuration.ingress.fqdn @description('The name of the resource group the Container App was deployed into.') output resourceGroupName string = resourceGroup().name @@ -289,6 +340,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -337,6 +391,25 @@ type container = { volumeMounts: volumeMount[]? } +type ingressPortMapping = { + @description('Optional. Specifies the exposed port for the target port. If not specified, it defaults to target port.') + exposedPort: int? + + @description('Required. Specifies whether the app port is accessible outside of the environment.') + external: bool + + @description('Required. Specifies the port the container listens on.') + targetPort: int +} + +type serviceBind = { + @description('Required. The name of the service.') + name: string + + @description('Required. The service ID.') + serviceId: string +} + type environmentVar = { @description('Required. Environment variable name.') name: string @@ -387,6 +460,26 @@ type containerAppProbe = { type: ('Liveness' | 'Startup' | 'Readiness')? } +type corsPolicyType = { + @description('Optional. Switch to determine whether the resource allows credentials.') + allowCredentials: bool? + + @description('Optional. Specifies the content for the access-control-allow-headers header.') + allowedHeaders: string[]? + + @description('Optional. Specifies the content for the access-control-allow-methods header.') + allowedMethods: string[]? + + @description('Optional. Specifies the content for the access-control-allow-origins header.') + allowedOrigins: string[]? + + @description('Optional. Specifies the content for the access-control-expose-headers header.') + exposeHeaders: string[]? + + @description('Optional. Specifies the content for the access-control-max-age header.') + maxAge: int? +}? + type containerAppProbeHttpGet = { @description('Optional. Host name to connect to. Defaults to the pod IP.') host: string? diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json index 34b2a94817..be0ad1bf8b 100644 --- a/avm/res/app/container-app/main.json +++ b/avm/res/app/container-app/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8728217426108975166" + "version": "0.29.47.4906", + "templateHash": "7992196126209120702" }, "name": "Container Apps", "description": "This module deploys a Container App.", @@ -66,6 +66,13 @@ "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": { @@ -201,6 +208,47 @@ } } }, + "ingressPortMapping": { + "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." + } + } + } + }, + "serviceBind": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the service." + } + }, + "serviceId": { + "type": "string", + "metadata": { + "description": "Required. The service ID." + } + } + } + }, "environmentVar": { "type": "object", "properties": { @@ -309,6 +357,66 @@ } } }, + "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." + } + } + }, + "nullable": true + }, "containerAppProbeHttpGet": { "type": "object", "properties": { @@ -430,6 +538,13 @@ "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, @@ -437,6 +552,24 @@ "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", + "metadata": { + "description": "Optional. Object userd to configure CORS policy." + } + }, "stickySessionsAffinity": { "type": "string", "defaultValue": "none", @@ -461,6 +594,30 @@ "description": "Optional. Ingress transport protocol." } }, + "service": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Dev ContainerApp service type." + } + }, + "includeAddOns": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Toggle to include the service configuration." + } + }, + "additionalPortMappings": { + "type": "array", + "items": { + "$ref": "#/definitions/ingressPortMapping" + }, + "nullable": true, + "metadata": { + "description": "Optional. Settings to expose additional ports on container app." + } + }, "ingressAllowInsecure": { "type": "bool", "defaultValue": true, @@ -496,6 +653,16 @@ "description": "Optional. Scaling rules." } }, + "serviceBinds": { + "type": "array", + "items": { + "$ref": "#/definitions/serviceBind" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of container app services bound to the app." + } + }, "activeRevisionsMode": { "type": "string", "defaultValue": "Single", @@ -507,7 +674,7 @@ "description": "Optional. Controls how active revisions are handled for the Container app." } }, - "environmentId": { + "environmentResourceId": { "type": "string", "metadata": { "description": "Required. Resource ID of environment." @@ -661,6 +828,13 @@ } }, "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)))))]" + } + ], "secretList": "[if(not(empty(parameters('secrets'))), parameters('secrets').secureList, createArray())]", "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())]", @@ -696,36 +870,18 @@ }, "containerApp": { "type": "Microsoft.App/containerApps", - "apiVersion": "2023-05-01", + "apiVersion": "2024-03-01", "name": "[parameters('name')]", "tags": "[parameters('tags')]", "location": "[parameters('location')]", "identity": "[variables('identity')]", "properties": { - "environmentId": "[parameters('environmentId')]", + "environmentId": "[parameters('environmentResourceId')]", "configuration": { "activeRevisionsMode": "[parameters('activeRevisionsMode')]", "dapr": "[if(not(empty(parameters('dapr'))), parameters('dapr'), null())]", - "ingress": { - "allowInsecure": "[parameters('ingressAllowInsecure')]", - "customDomains": "[if(not(empty(parameters('customDomains'))), parameters('customDomains'), null())]", - "exposedPort": "[parameters('exposedPort')]", - "external": "[parameters('ingressExternal')]", - "ipSecurityRestrictions": "[if(not(empty(parameters('ipSecurityRestrictions'))), parameters('ipSecurityRestrictions'), null())]", - "targetPort": "[parameters('ingressTargetPort')]", - "stickySessions": { - "affinity": "[parameters('stickySessionsAffinity')]" - }, - "traffic": [ - { - "label": "[parameters('trafficLabel')]", - "latestRevision": "[parameters('trafficLatestRevision')]", - "revisionName": "[parameters('trafficRevisionName')]", - "weight": "[parameters('trafficWeight')]" - } - ], - "transport": "[parameters('ingressTransport')]" - }, + "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": "[variables('secretList')]" @@ -739,6 +895,7 @@ "minReplicas": "[parameters('scaleMinReplicas')]", "rules": "[if(not(empty(parameters('scaleRules'))), parameters('scaleRules'), null())]" }, + "serviceBinds": "[if(and(parameters('includeAddOns'), not(empty(parameters('serviceBinds')))), parameters('serviceBinds'), null())]", "volumes": "[if(not(empty(parameters('volumes'))), parameters('volumes'), null())]" }, "workloadProfileName": "[parameters('workloadProfileName')]" @@ -761,20 +918,20 @@ "containerApp_roleAssignments": { "copy": { "name": "containerApp_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.App/containerApps/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.App/containerApps', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -794,7 +951,7 @@ "metadata": { "description": "The configuration of ingress fqdn." }, - "value": "[reference('containerApp').configuration.ingress.fqdn]" + "value": "[if(parameters('disableIngress'), 'IngressDisabled', reference('containerApp').configuration.ingress.fqdn)]" }, "resourceGroupName": { "type": "string", @@ -815,14 +972,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('containerApp', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('containerApp', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('containerApp', '2023-05-01', 'full').location]" + "value": "[reference('containerApp', '2024-03-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep index 4a905930e7..c534e47cb3 100644 --- a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep @@ -51,7 +51,7 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - environmentId: nestedDependencies.outputs.managedEnvironmentResourceId + environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId location: resourceLocation containers: [ { diff --git a/avm/res/app/container-app/tests/e2e/disable-ingress/dependencies.bicep b/avm/res/app/container-app/tests/e2e/disable-ingress/dependencies.bicep new file mode 100644 index 0000000000..350d56b26f --- /dev/null +++ b/avm/res/app/container-app/tests/e2e/disable-ingress/dependencies.bicep @@ -0,0 +1,14 @@ +@description('Required. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Environment to create.') +param managedEnvironmentName string + +resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { + name: managedEnvironmentName + location: location + properties: {} +} + +@description('The resource ID of the created Managed Environment.') +output managedEnvironmentResourceId string = managedEnvironment.id diff --git a/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep new file mode 100644 index 0000000000..ffe04696b9 --- /dev/null +++ b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep @@ -0,0 +1,70 @@ +targetScope = 'subscription' + +metadata name = 'Without ingress enabled' +metadata description = 'This instance deploys the module with ingress traffic completely disabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.containerApps-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'acapriv' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-paramNested' + params: { + location: resourceLocation + managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId + location: resourceLocation + disableIngress: true + containers: [ + { + name: 'simple-hello-world-container' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + resources: { + // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 + cpu: json('0.25') + memory: '0.5Gi' + } + } + ] + } + } +] diff --git a/avm/res/app/container-app/tests/e2e/max/dependencies.bicep b/avm/res/app/container-app/tests/e2e/max/dependencies.bicep index 0ca3555668..a0eb1fe285 100644 --- a/avm/res/app/container-app/tests/e2e/max/dependencies.bicep +++ b/avm/res/app/container-app/tests/e2e/max/dependencies.bicep @@ -4,6 +4,16 @@ param location string = resourceGroup().location @description('Required. The name of the Managed Environment for Container Apps to create.') param managedEnvironmentName string +@description('Required. The name of the Key Vault referenced by the Container Apps.') +param keyVaultName string + +@description('Required. The name of the Key Vault referenced by the Container Apps.') +param keyVaultSecretName string + +@description('Optional. Key vault stored secret to pass into environment variables. The value is a GUID.') +@secure() +param myCustomKeyVaultSecret string = newGuid() + @description('Required. The name of the managed identity to create.') param managedIdentityName string @@ -17,16 +27,60 @@ resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { } } +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: subscription().tenantId + publicNetworkAccess: 'Enabled' + enableRbacAuthorization: true + } +} + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + parent: keyVault + name: keyVaultSecretName + properties: { + value: myCustomKeyVaultSecret + } +} + resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: managedIdentityName location: location } +@description('The the built-in Key Vault Secret User role definition.') +resource roleDefinitionKeyVault 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + scope: subscription() + name: '4633458b-17de-408a-b874-0445c86b69e6' +} + +resource roleAssignmentKeyVault 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, managedIdentity.id) + scope: keyVault + properties: { + roleDefinitionId: roleDefinitionKeyVault.id + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Managed Environment.') +output managedEnvironmentResourceId string = managedEnvironment.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The key vault secret URI.') +output keyVaultSecretURI string = keyVaultSecret.properties.secretUri + @description('The resource ID of the created Managed Identity.') output managedIdentityResourceId string = managedIdentity.id @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId - -@description('The resource ID of the created Managed Environment.') -output managedEnvironmentResourceId string = managedEnvironment.id diff --git a/avm/res/app/container-app/tests/e2e/max/main.test.bicep b/avm/res/app/container-app/tests/e2e/max/main.test.bicep index 0808b81711..8c5ac2b4d4 100644 --- a/avm/res/app/container-app/tests/e2e/max/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/max/main.test.bicep @@ -20,6 +20,10 @@ param serviceShort string = 'acamax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Optional. Container app stored secret to pass into environment variables. The value is a GUID.') +@secure() +param myCustomContainerAppSecret string = newGuid() + // =========== // // Deployments // // =========== // @@ -37,6 +41,8 @@ module nestedDependencies 'dependencies.bicep' = { params: { location: resourceLocation managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + keyVaultSecretName: 'dep-${namePrefix}-kv-secret-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' } } @@ -58,11 +64,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'e9bac1ee-aebe-4513-9337-49e87a7be05e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -76,7 +84,7 @@ module testDeployment '../../../main.bicep' = [ principalType: 'ServicePrincipal' } ] - environmentId: nestedDependencies.outputs.managedEnvironmentResourceId + environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId location: resourceLocation lock: { kind: 'CanNotDelete' @@ -90,8 +98,13 @@ module testDeployment '../../../main.bicep' = [ secrets: { secureList: [ { - name: 'customtest' - value: guid(deployment().name) + name: 'containerappstoredsecret' + value: myCustomContainerAppSecret + } + { + name: 'keyvaultstoredsecret' + keyVaultUrl: nestedDependencies.outputs.keyVaultSecretURI + identity: nestedDependencies.outputs.managedIdentityResourceId } ] } @@ -104,6 +117,16 @@ module testDeployment '../../../main.bicep' = [ cpu: json('0.25') memory: '0.5Gi' } + env: [ + { + name: 'ContainerAppStoredSecretName' + secretRef: 'containerappstoredsecret' + } + { + name: 'ContainerAppKeyVaultStoredSecretName' + secretRef: 'keyvaultstoredsecret' + } + ] probes: [ { type: 'Liveness' diff --git a/avm/res/app/container-app/tests/e2e/vnet/dependencies.bicep b/avm/res/app/container-app/tests/e2e/vnet/dependencies.bicep new file mode 100644 index 0000000000..4c388edfe1 --- /dev/null +++ b/avm/res/app/container-app/tests/e2e/vnet/dependencies.bicep @@ -0,0 +1,39 @@ +@description('Required. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Environment to create.') +param managedEnvironmentName string + +resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { + name: managedEnvironmentName + location: location + properties: { + vnetConfiguration: { + internal: true + infrastructureSubnetId: subnet.id + } + } +} + +resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' = { + name: '${managedEnvironmentName}-vnet' + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/16' + ] + } + } +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-01-01' = { + name: 'default' + parent: vnet + properties: { + addressPrefix: '10.0.0.0/23' + } +} + +@description('The resource ID of the created Managed Environment.') +output managedEnvironmentResourceId string = managedEnvironment.id diff --git a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep new file mode 100644 index 0000000000..3c34893f8c --- /dev/null +++ b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep @@ -0,0 +1,81 @@ +targetScope = 'subscription' + +metadata name = 'VNet integrated container app deployment' +metadata description = 'This instance deploys the container app in a managed environment with a virtual network using TCP ingress.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-app.containerApps-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'acavnet' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-paramNested' + params: { + location: resourceLocation + managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId + location: resourceLocation + ingressExternal: false + ingressTransport: 'tcp' + ingressAllowInsecure: false + ingressTargetPort: 80 + additionalPortMappings: [ + { + external: false + targetPort: 8080 + exposedPort: 8080 + } + ] + + containers: [ + { + name: 'simple-hello-world-container' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + resources: { + // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 + cpu: json('0.25') + memory: '0.5Gi' + } + } + ] + } + } +] diff --git a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep index 9deb2baa79..a25b1ec60d 100644 --- a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep @@ -58,7 +58,7 @@ module testDeployment '../../../main.bicep' = [ 'hidden-title': 'This is visible in the resource name' Env: 'test' } - environmentId: nestedDependencies.outputs.managedEnvironmentResourceId + environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId location: resourceLocation lock: { kind: 'CanNotDelete' diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json index aa34c4d0f5..d96f771b50 100644 --- a/avm/res/app/container-app/version.json +++ b/avm/res/app/container-app/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.10", "pathFilters": [ "./main.json" ] diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md index b4bdd8f3ec..0b3ed34ec0 100644 --- a/avm/res/app/job/README.md +++ b/avm/res/app/job/README.md @@ -15,7 +15,7 @@ This module deploys a Container App Job. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.App/jobs` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/jobs) | +| `Microsoft.App/jobs` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-03-01/jobs) | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | @@ -307,11 +307,13 @@ module job 'br/public:avm/res/app/job:' = { } roleAssignments: [ { + name: 'be1bb251-6a44-49f7-8658-d836d0049fc4' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -478,11 +480,13 @@ module job 'br/public:avm/res/app/job:' = { "roleAssignments": { "value": [ { + "name": "be1bb251-6a44-49f7-8658-d836d0049fc4", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1755,6 +1759,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1805,6 +1810,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/app/job/main.bicep b/avm/res/app/job/main.bicep index f237c49b71..246567f010 100644 --- a/avm/res/app/job/main.bicep +++ b/avm/res/app/job/main.bicep @@ -165,6 +165,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.app-job.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -221,15 +232,11 @@ resource job_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ? scope: job } -resource automationAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(job.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) +resource job_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(job.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -296,6 +303,9 @@ type lockType = { } type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/app/job/main.json b/avm/res/app/job/main.json index 44afb314c5..ec8cecef40 100644 --- a/avm/res/app/job/main.json +++ b/avm/res/app/job/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2845806145425627201" + "version": "0.29.47.4906", + "templateHash": "3241416378089568183" }, "name": "Container App Jobs", "description": "This module deploys a Container App Job.", @@ -64,6 +64,13 @@ "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": { @@ -932,6 +939,13 @@ } }, "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": { @@ -1005,23 +1019,23 @@ "job" ] }, - "automationAccount_roleAssignments": { + "job_roleAssignments": { "copy": { - "name": "automationAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "job_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "job" diff --git a/avm/res/app/job/tests/e2e/max/main.test.bicep b/avm/res/app/job/tests/e2e/max/main.test.bicep index b31e72f824..93ed3f17f5 100644 --- a/avm/res/app/job/tests/e2e/max/main.test.bicep +++ b/avm/res/app/job/tests/e2e/max/main.test.bicep @@ -181,11 +181,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'be1bb251-6a44-49f7-8658-d836d0049fc4' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/app/job/version.json b/avm/res/app/job/version.json index b3d560b1ad..3f863a2bec 100644 --- a/avm/res/app/job/version.json +++ b/avm/res/app/job/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/app/managed-environment/README.md b/avm/res/app/managed-environment/README.md index 6f20e777cf..1884561a9a 100644 --- a/avm/res/app/managed-environment/README.md +++ b/avm/res/app/managed-environment/README.md @@ -15,7 +15,8 @@ This module deploys an App Managed Environment (also known as a Container App En | Resource Type | API Version | | :-- | :-- | -| `Microsoft.App/managedEnvironments` | [2023-11-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-11-02-preview/managedEnvironments) | +| `Microsoft.App/managedEnvironments` | [2024-02-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-02-02-preview/managedEnvironments) | +| `Microsoft.App/managedEnvironments/storages` | [2024-02-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-02-02-preview/managedEnvironments/storages) | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | @@ -142,6 +143,7 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' logAnalyticsWorkspaceResourceId: '' name: 'amemax001' // Non-required parameters + appInsightsConnectionString: '' dockerBridgeCidr: '172.16.0.1/28' infrastructureResourceGroupName: '' infrastructureSubnetId: '' @@ -157,15 +159,30 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' '' ] } + openTelemetryConfiguration: { + logsConfiguration: { + destinations: [ + 'appInsights' + ] + } + tracesConfiguration: { + destinations: [ + 'appInsights' + ] + } + } + peerTrafficEncryption: true platformReservedCidr: '172.17.17.0/24' platformReservedDnsIP: '172.17.17.17' roleAssignments: [ { + name: '43fc5250-f111-472b-8722-f1cb4a0e754b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -176,6 +193,20 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' roleDefinitionIdOrName: '' } ] + storages: [ + { + accessMode: 'ReadWrite' + kind: 'SMB' + shareName: 'smbfileshare' + storageAccountName: '' + } + { + accessMode: 'ReadWrite' + kind: 'NFS' + shareName: 'nfsfileshare' + storageAccountName: '' + } + ] tags: { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -212,6 +243,9 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' "value": "amemax001" }, // Non-required parameters + "appInsightsConnectionString": { + "value": "" + }, "dockerBridgeCidr": { "value": "172.16.0.1/28" }, @@ -241,6 +275,23 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' ] } }, + "openTelemetryConfiguration": { + "value": { + "logsConfiguration": { + "destinations": [ + "appInsights" + ] + }, + "tracesConfiguration": { + "destinations": [ + "appInsights" + ] + } + } + }, + "peerTrafficEncryption": { + "value": true + }, "platformReservedCidr": { "value": "172.17.17.0/24" }, @@ -250,11 +301,13 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' "roleAssignments": { "value": [ { + "name": "43fc5250-f111-472b-8722-f1cb4a0e754b", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -266,6 +319,22 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' } ] }, + "storages": { + "value": [ + { + "accessMode": "ReadWrite", + "kind": "SMB", + "shareName": "smbfileshare", + "storageAccountName": "" + }, + { + "accessMode": "ReadWrite", + "kind": "NFS", + "shareName": "nfsfileshare", + "storageAccountName": "" + } + ] + }, "tags": { "value": { "Env": "test", @@ -465,6 +534,7 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | Parameter | Type | Description | | :-- | :-- | :-- | +| [`appInsightsConnectionString`](#parameter-appinsightsconnectionstring) | securestring | Application Insights connection string. | | [`certificatePassword`](#parameter-certificatepassword) | securestring | Password of the certificate used by the custom domain. | | [`certificateValue`](#parameter-certificatevalue) | securestring | Certificate to use for the custom domain. PFX or PEM. | | [`daprAIConnectionString`](#parameter-dapraiconnectionstring) | securestring | Application Insights connection string used by Dapr to export Service to Service communication telemetry. | @@ -475,7 +545,10 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`logsDestination`](#parameter-logsdestination) | string | Logs destination. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`openTelemetryConfiguration`](#parameter-opentelemetryconfiguration) | object | Open Telemetry configuration. | +| [`peerTrafficEncryption`](#parameter-peertrafficencryption) | bool | Whether or not to encrypt peer traffic. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`storages`](#parameter-storages) | array | The list of storages to mount on the environment. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not this Managed Environment is zone-redundant. | @@ -549,6 +622,14 @@ Workload profiles configured for the Managed Environment. Required if zoneRedund - Type: array - Default: `[]` +### Parameter: `appInsightsConnectionString` + +Application Insights connection string. + +- Required: No +- Type: securestring +- Default: `''` + ### Parameter: `certificatePassword` Password of the certificate used by the custom domain. @@ -677,6 +758,22 @@ The resource ID(s) to assign to the resource. - Required: No - Type: array +### Parameter: `openTelemetryConfiguration` + +Open Telemetry configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `peerTrafficEncryption` + +Whether or not to encrypt peer traffic. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `roleAssignments` Array of role assignments to create. @@ -699,6 +796,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -749,6 +847,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -766,6 +871,64 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `storages` + +The list of storages to mount on the environment. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessMode`](#parameter-storagesaccessmode) | string | Access mode for storage: "ReadOnly" or "ReadWrite". | +| [`kind`](#parameter-storageskind) | string | Type of storage: "SMB" or "NFS". | +| [`shareName`](#parameter-storagessharename) | string | File share name. | +| [`storageAccountName`](#parameter-storagesstorageaccountname) | string | Storage account name. | + +### Parameter: `storages.accessMode` + +Access mode for storage: "ReadOnly" or "ReadWrite". + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'ReadOnly' + 'ReadWrite' + ] + ``` + +### Parameter: `storages.kind` + +Type of storage: "SMB" or "NFS". + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'NFS' + 'SMB' + ] + ``` + +### Parameter: `storages.shareName` + +File share name. + +- Required: Yes +- Type: string + +### Parameter: `storages.storageAccountName` + +Storage account name. + +- Required: Yes +- Type: string + ### Parameter: `tags` Tags of the resource. diff --git a/avm/res/app/managed-environment/main.bicep b/avm/res/app/managed-environment/main.bicep index ef26dcc7ae..07c6dc33af 100644 --- a/avm/res/app/managed-environment/main.bicep +++ b/avm/res/app/managed-environment/main.bicep @@ -26,6 +26,10 @@ param logsDestination string = 'log-analytics' @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true +@description('Optional. Application Insights connection string.') +@secure() +param appInsightsConnectionString string = '' + @description('Optional. Application Insights connection string used by Dapr to export Service to Service communication telemetry.') @secure() param daprAIConnectionString string = '' @@ -49,6 +53,9 @@ param platformReservedCidr string = '' @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.') param platformReservedDnsIP string = '' +@description('Optional. Whether or not to encrypt peer traffic.') +param peerTrafficEncryption bool = true + @description('Optional. Whether or not this Managed Environment is zone-redundant.') param zoneRedundant bool = true @@ -66,12 +73,18 @@ param dnsSuffix string = '' @description('Optional. The lock settings of the service.') param lock lockType +@description('Optional. Open Telemetry configuration.') +param openTelemetryConfiguration object = {} + @description('Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant.') param workloadProfiles array = [] @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.') param infrastructureResourceGroupName string = take('ME_${name}', 63) +@description('Optional. The list of storages to mount on the environment.') +param storages storageType + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -101,6 +114,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.app-managedenvironment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -120,17 +144,20 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(logAnalyticsWorkspaceResourceId)) { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = if (!empty(logAnalyticsWorkspaceResourceId)) { name: last(split(logAnalyticsWorkspaceResourceId, '/'))! scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4]) } -resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-11-02-preview' = { +resource managedEnvironment 'Microsoft.App/managedEnvironments@2024-02-02-preview' = { name: name location: location tags: tags identity: identity properties: { + appInsightsConfiguration: { + connectionString: appInsightsConnectionString + } appLogsConfiguration: { destination: logsDestination logAnalyticsConfiguration: { @@ -145,6 +172,12 @@ resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-11-02-previe certificateValue: !empty(certificateValue) ? certificateValue : null dnsSuffix: dnsSuffix } + openTelemetryConfiguration: !empty(openTelemetryConfiguration) ? openTelemetryConfiguration : null + peerTrafficConfiguration: { + encryption: { + enabled: peerTrafficEncryption + } + } vnetConfiguration: { internal: internal infrastructureSubnetId: !empty(infrastructureSubnetId) ? infrastructureSubnetId : null @@ -156,17 +189,43 @@ resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-11-02-previe zoneRedundant: zoneRedundant infrastructureResourceGroup: infrastructureResourceGroupName } + + resource storage 'storages' = [ + for storage in (storages ?? []): { + name: storage.shareName + properties: { + nfsAzureFile: storage.kind == 'NFS' + ? { + accessMode: storage.accessMode + server: '${storage.storageAccountName}.file.${environment().suffixes.storage}' + shareName: '/${storage.storageAccountName}/${storage.shareName}' + } + : null + azureFile: storage.kind == 'SMB' + ? { + accessMode: storage.accessMode + accountName: storage.storageAccountName + accountKey: listkeys( + resourceId('Microsoft.Storage/storageAccounts', storage.storageAccountName), + '2023-01-01' + ).keys[0].value + shareName: storage.shareName + } + : null + } + } + ] } resource managedEnvironment_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(managedEnvironment.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + managedEnvironment.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -231,6 +290,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -252,3 +314,17 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +type storageType = { + @description('Required. Access mode for storage: "ReadOnly" or "ReadWrite".') + accessMode: ('ReadOnly' | 'ReadWrite') + + @description('Required. Type of storage: "SMB" or "NFS".') + kind: ('SMB' | 'NFS') + + @description('Required. Storage account name.') + storageAccountName: string + + @description('Required. File share name.') + shareName: string +}[]? diff --git a/avm/res/app/managed-environment/main.json b/avm/res/app/managed-environment/main.json index 8926c804ee..ed0df72869 100644 --- a/avm/res/app/managed-environment/main.json +++ b/avm/res/app/managed-environment/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12999897331661318170" + "version": "0.29.47.4906", + "templateHash": "13961196804563097956" }, "name": "App ManagedEnvironments", "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", @@ -66,6 +66,13 @@ "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": { @@ -126,6 +133,47 @@ } }, "nullable": true + }, + "storageType": { + "type": "array", + "items": { + "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." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -181,6 +229,13 @@ "description": "Optional. Enable/Disable usage telemetry for module." } }, + "appInsightsConnectionString": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Application Insights connection string." + } + }, "daprAIConnectionString": { "type": "securestring", "defaultValue": "", @@ -230,6 +285,13 @@ "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." + } + }, "zoneRedundant": { "type": "bool", "defaultValue": true, @@ -264,6 +326,13 @@ "description": "Optional. The lock settings of the service." } }, + "openTelemetryConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Open Telemetry configuration." + } + }, "workloadProfiles": { "type": "array", "defaultValue": [], @@ -277,9 +346,22 @@ "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": { + "$ref": "#/definitions/storageType", + "metadata": { + "description": "Optional. The list of storages to mount on the 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": { @@ -291,6 +373,22 @@ } }, "resources": { + "managedEnvironment::storage": { + "copy": { + "name": "storage", + "count": "[length(coalesce(parameters('storages'), createArray()))]" + }, + "type": "Microsoft.App/managedEnvironments/storages", + "apiVersion": "2024-02-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", @@ -315,24 +413,27 @@ "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", "existing": true, "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-06-01", + "apiVersion": "2023-09-01", "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", "name": "[last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))]" }, "managedEnvironment": { "type": "Microsoft.App/managedEnvironments", - "apiVersion": "2023-11-02-preview", + "apiVersion": "2024-02-02-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "identity": "[variables('identity')]", "properties": { + "appInsightsConfiguration": { + "connectionString": "[parameters('appInsightsConnectionString')]" + }, "appLogsConfiguration": { "destination": "[parameters('logsDestination')]", "logAnalyticsConfiguration": { "customerId": "[reference('logAnalyticsWorkspace').customerId]", - "sharedKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))), '2021-06-01').primarySharedKey]" + "sharedKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))), '2023-09-01').primarySharedKey]" } }, "daprAIConnectionString": "[parameters('daprAIConnectionString')]", @@ -342,6 +443,12 @@ "certificateValue": "[if(not(empty(parameters('certificateValue'))), parameters('certificateValue'), null())]", "dnsSuffix": "[parameters('dnsSuffix')]" }, + "openTelemetryConfiguration": "[if(not(empty(parameters('openTelemetryConfiguration'))), parameters('openTelemetryConfiguration'), null())]", + "peerTrafficConfiguration": { + "encryption": { + "enabled": "[parameters('peerTrafficEncryption')]" + } + }, "vnetConfiguration": { "internal": "[parameters('internal')]", "infrastructureSubnetId": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('infrastructureSubnetId'), null())]", @@ -360,20 +467,20 @@ "managedEnvironment_roleAssignments": { "copy": { "name": "managedEnvironment_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.App/managedEnvironments/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.App/managedEnvironments', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -407,7 +514,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('managedEnvironment', '2023-11-02-preview', 'full').location]" + "value": "[reference('managedEnvironment', '2024-02-02-preview', 'full').location]" }, "name": { "type": "string", @@ -428,7 +535,7 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2023-11-02-preview', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2024-02-02-preview', 'full'), 'identity'), 'principalId'), '')]" }, "defaultDomain": { "type": "string", diff --git a/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep b/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep index ebeb013d5a..6c836f75d6 100644 --- a/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep +++ b/avm/res/app/managed-environment/tests/e2e/max/dependencies.bicep @@ -4,12 +4,18 @@ param location string = resourceGroup().location @description('Required. The name of the Log Analytics Workspace to create.') param logAnalyticsWorkspaceName string +@description('Required. The name of the Application Insights Component to create.') +param appInsightsComponentName string + @description('Required. The name of the Virtual Network to create.') param virtualNetworkName string @description('Required. The name of the Managed Identity to create.') param managedIdentityName string +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + var addressPrefix = '10.0.0.0/16' resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { @@ -26,6 +32,16 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 }) } +resource appInsightsComponent 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightsComponentName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { name: virtualNetworkName location: location @@ -39,7 +55,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'defaultSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 23, 0) delegations: [ { name: 'Microsoft.App.environments' @@ -48,6 +64,11 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } } ] + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] } } ] @@ -59,6 +80,48 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018- location: location } +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Premium_LRS' + } + kind: 'FileStorage' + properties: { + publicNetworkAccess: 'Enabled' + supportsHttpsTrafficOnly: false + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, 'defaultSubnet') + } + ] + } + } + + resource fileService 'fileServices@2023-01-01' = { + name: 'default' + + resource smbfileshare 'shares@2023-01-01' = { + name: 'smbfileshare' + properties: { + enabledProtocols: 'SMB' + shareQuota: 100 + } + } + + resource azureFileNFS 'shares@2023-01-01' = { + name: 'nfsfileshare' + properties: { + enabledProtocols: 'NFS' + shareQuota: 100 + } + } + } +} + @description('The resource ID of the created Log Analytics Workspace.') output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id @@ -70,3 +133,9 @@ output managedIdentityPrincipalId string = managedIdentity.properties.principalI @description('The resource ID of the created Managed Identity.') output managedIdentityResourceId string = managedIdentity.id + +@description('The Connection String of the created Application Insights Component.') +output appInsightsConnectionString string = appInsightsComponent.properties.ConnectionString + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name diff --git a/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep b/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep index 919c7ab40f..582c1734c7 100644 --- a/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep +++ b/avm/res/app/managed-environment/tests/e2e/max/main.test.bicep @@ -38,6 +38,8 @@ module nestedDependencies 'dependencies.bicep' = { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' location: resourceLocation managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + appInsightsComponentName: 'dep-${namePrefix}-appinsights-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' } } @@ -54,6 +56,7 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' logAnalyticsWorkspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId location: resourceLocation + appInsightsConnectionString: nestedDependencies.outputs.appInsightsConnectionString workloadProfiles: [ { workloadProfileType: 'D4' @@ -64,6 +67,7 @@ module testDeployment '../../../main.bicep' = [ ] internal: true dockerBridgeCidr: '172.16.0.1/28' + peerTrafficEncryption: true platformReservedCidr: '172.17.17.0/24' platformReservedDnsIP: '172.17.17.17' infrastructureSubnetId: nestedDependencies.outputs.subnetResourceId @@ -74,13 +78,23 @@ module testDeployment '../../../main.bicep' = [ nestedDependencies.outputs.managedIdentityResourceId ] } + openTelemetryConfiguration: { + tracesConfiguration: { + destinations: ['appInsights'] + } + logsConfiguration: { + destinations: ['appInsights'] + } + } roleAssignments: [ { + name: '43fc5250-f111-472b-8722-f1cb4a0e754b' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -95,6 +109,21 @@ module testDeployment '../../../main.bicep' = [ } ] + storages: [ + { + kind: 'SMB' + shareName: 'smbfileshare' + accessMode: 'ReadWrite' + storageAccountName: nestedDependencies.outputs.storageAccountName + } + { + kind: 'NFS' + shareName: 'nfsfileshare' + accessMode: 'ReadWrite' + storageAccountName: nestedDependencies.outputs.storageAccountName + } + ] + lock: { kind: 'CanNotDelete' name: 'myCustomLockName' diff --git a/avm/res/app/managed-environment/version.json b/avm/res/app/managed-environment/version.json index 04a0dd1a80..35040975ae 100644 --- a/avm/res/app/managed-environment/version.json +++ b/avm/res/app/managed-environment/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/automation/automation-account/README.md b/avm/res/automation/automation-account/README.md index c4afb6b6b5..79394bbca3 100644 --- a/avm/res/automation/automation-account/README.md +++ b/avm/res/automation/automation-account/README.md @@ -26,8 +26,8 @@ This module deploys an Azure Automation Account. | `Microsoft.Automation/automationAccounts/softwareUpdateConfigurations` | [2019-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/softwareUpdateConfigurations) | | `Microsoft.Automation/automationAccounts/variables` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/variables) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | | `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | @@ -236,9 +236,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'Webhook' subnetResourceId: '' tags: { @@ -248,9 +252,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'Webhook' subnetResourceId: '' tags: { @@ -260,9 +268,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'DSCAndHybridWorker' subnetResourceId: '' tags: { @@ -274,11 +286,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -497,9 +511,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "Webhook", "subnetResourceId": "", "tags": { @@ -509,9 +527,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "Webhook", "subnetResourceId": "", "tags": { @@ -521,9 +543,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "DSCAndHybridWorker", "subnetResourceId": "", "tags": { @@ -537,11 +563,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -739,9 +767,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'Webhook' subnetResourceId: '' tags: { @@ -751,9 +783,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'DSCAndHybridWorker' subnetResourceId: '' tags: { @@ -955,9 +991,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "Webhook", "subnetResourceId": "", "tags": { @@ -967,9 +1007,13 @@ module automationAccount 'br/public:avm/res/automation/automation-account:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "DSCAndHybridWorker", "subnetResourceId": "", "tags": { @@ -1541,8 +1585,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1580,19 +1623,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -1645,7 +1688,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -1663,7 +1706,7 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string @@ -1732,19 +1775,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1782,6 +1870,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1832,6 +1921,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1894,6 +1990,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1944,6 +2041,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2022,6 +2126,7 @@ List of variables to be created in the automation account. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the deployed automation account. | +| `privateEndpoints` | array | The private endpoints of the automation account. | | `resourceGroupName` | string | The resource group of the deployed automation account. | | `resourceId` | string | The resource ID of the deployed automation account. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -2032,8 +2137,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `res/operational-insights/workspace/linked-service` | Local reference | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | | `br/public:avm/res/operations-management/solution:0.1.0` | Remote reference | ## Data Collection diff --git a/avm/res/automation/automation-account/credential/main.json b/avm/res/automation/automation-account/credential/main.json index 4540f93940..a805bd02fe 100644 --- a/avm/res/automation/automation-account/credential/main.json +++ b/avm/res/automation/automation-account/credential/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17311442241252857733" + "version": "0.29.47.4906", + "templateHash": "10580987489047715400" }, "name": "Automation Account Credential", "description": "This module deploys Azure Automation Account Credential.", diff --git a/avm/res/automation/automation-account/job-schedule/main.json b/avm/res/automation/automation-account/job-schedule/main.json index 7937f0cc40..0b9b9b697e 100644 --- a/avm/res/automation/automation-account/job-schedule/main.json +++ b/avm/res/automation/automation-account/job-schedule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7852489714477748054" + "version": "0.29.47.4906", + "templateHash": "10826746631810286901" }, "name": "Automation Account Job Schedules", "description": "This module deploys an Azure Automation Account Job Schedule.", diff --git a/avm/res/automation/automation-account/main.bicep b/avm/res/automation/automation-account/main.bicep index 167586f0b2..d50ac1b2c2 100644 --- a/avm/res/automation/automation-account/main.bicep +++ b/avm/res/automation/automation-account/main.bicep @@ -123,6 +123,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.automation-automationaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -282,7 +293,7 @@ module automationAccount_variables 'variable/main.bicep' = [ } ] -module automationAccount_linkedService '../../operational-insights/workspace/linked-service/main.bicep' = if (!empty(linkedWorkspaceResourceId)) { +module automationAccount_linkedService 'modules/linked-service.bicep' = if (!empty(linkedWorkspaceResourceId)) { name: '${uniqueString(deployment().name, location)}-AutoAccount-LinkedService' params: { name: 'automation' @@ -453,7 +464,7 @@ resource automationAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSett } ] -module automationAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module automationAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-automationAccount-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -494,8 +505,7 @@ module automationAccount_privateEndpoints 'br/public:avm/res/network/private-end 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -507,14 +517,14 @@ module automationAccount_privateEndpoints 'br/public:avm/res/network/private-end ] resource automationAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(automationAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + automationAccount.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -541,6 +551,17 @@ output systemAssignedMIPrincipalId string = automationAccount.?identity.?princip @description('The location the resource was deployed into.') output location string = automationAccount.location +@description('The private endpoints of the automation account.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: automationAccount_privateEndpoints[i].outputs.name + resourceId: automationAccount_privateEndpoints[i].outputs.resourceId + groupId: automationAccount_privateEndpoints[i].outputs.groupId + customDnsConfig: automationAccount_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: automationAccount_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -562,6 +583,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -600,11 +624,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @@ -615,10 +648,10 @@ type privateEndpointType = { @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -635,7 +668,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? diff --git a/avm/res/automation/automation-account/main.json b/avm/res/automation/automation-account/main.json index f996eaaf57..5de3a4f57b 100644 --- a/avm/res/automation/automation-account/main.json +++ b/avm/res/automation/automation-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15361181633952207113" + "version": "0.29.47.4906", + "templateHash": "7361377079098323612" }, "name": "Automation Accounts", "description": "This module deploys an Azure Automation Account.", @@ -66,6 +66,13 @@ "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": { @@ -165,21 +172,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -206,7 +236,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -215,7 +245,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -254,7 +284,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -673,6 +703,13 @@ } }, "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": { @@ -816,20 +853,20 @@ "automationAccount_roleAssignments": { "copy": { "name": "automationAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Automation/automationAccounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "automationAccount" @@ -860,8 +897,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17311442241252857733" + "version": "0.29.47.4906", + "templateHash": "10580987489047715400" }, "name": "Automation Account Credential", "description": "This module deploys Azure Automation Account Credential.", @@ -1019,8 +1056,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1661319858724328950" + "version": "0.29.47.4906", + "templateHash": "14556422655915492083" }, "name": "Automation Account Modules", "description": "This module deploys an Azure Automation Account Module.", @@ -1161,8 +1198,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11730927882334055011" + "version": "0.29.47.4906", + "templateHash": "6299933839999486418" }, "name": "Automation Account Schedules", "description": "This module deploys an Azure Automation Account Schedule.", @@ -1340,8 +1377,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16990805873972671446" + "version": "0.29.47.4906", + "templateHash": "11653746330709835761" }, "name": "Automation Account Runbooks", "description": "This module deploys an Azure Automation Account Runbook.", @@ -1544,8 +1581,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7852489714477748054" + "version": "0.29.47.4906", + "templateHash": "10826746631810286901" }, "name": "Automation Account Job Schedules", "description": "This module deploys an Azure Automation Account Job Schedule.", @@ -1672,8 +1709,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17408217027458282021" + "version": "0.29.47.4906", + "templateHash": "13284693609482301780" }, "name": "Automation Account Variables", "description": "This module deploys an Azure Automation Account Variable.", @@ -1787,8 +1824,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11327223922004680974" + "version": "0.29.47.4906", + "templateHash": "12032441371027552374" }, "name": "Log Analytics Workspace Linked Services", "description": "This module deploys a Log Analytics Workspace Linked Service.", @@ -2106,8 +2143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5046214814278705138" + "version": "0.29.47.4906", + "templateHash": "752305553206238634" }, "name": "Automation Account Software Update Configurations", "description": "This module deploys an Azure Automation Account Software Update Configuration.", @@ -2543,11 +2580,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2575,19 +2609,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2691,13 +2753,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2731,8 +2793,11 @@ "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." + "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": { @@ -2772,8 +2837,11 @@ "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." + "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": { @@ -2821,6 +2889,29 @@ } }, "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": { @@ -2856,18 +2947,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2923,6 +3007,13 @@ } }, "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')]", @@ -2940,8 +3031,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2959,7 +3050,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3000,27 +3091,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -3031,28 +3122,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -3060,12 +3175,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -3079,27 +3197,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -3157,14 +3284,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -3209,6 +3350,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('automationAccount', '2022-08-08', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the automation account." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('automationAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('automationAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('automationAccount_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('automationAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('automationAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/automation/automation-account/module/main.json b/avm/res/automation/automation-account/module/main.json index 5a3010f487..a84aab8636 100644 --- a/avm/res/automation/automation-account/module/main.json +++ b/avm/res/automation/automation-account/module/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1661319858724328950" + "version": "0.29.47.4906", + "templateHash": "14556422655915492083" }, "name": "Automation Account Modules", "description": "This module deploys an Azure Automation Account Module.", diff --git a/avm/res/automation/automation-account/modules/linked-service.bicep b/avm/res/automation/automation-account/modules/linked-service.bicep new file mode 100644 index 0000000000..ffe1bc4c99 --- /dev/null +++ b/avm/res/automation/automation-account/modules/linked-service.bicep @@ -0,0 +1,41 @@ +metadata name = 'Log Analytics Workspace Linked Services' +metadata description = 'This module deploys a Log Analytics Workspace Linked Service.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the link.') +param name string + +@description('Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access.') +param resourceId string = '' + +@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.') +param writeAccessResourceId string = '' + +@description('Optional. Tags to configure in the resource.') +param tags object? + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource linkedService 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = { + name: name + parent: workspace + tags: tags + properties: { + resourceId: resourceId + writeAccessResourceId: empty(writeAccessResourceId) ? null : writeAccessResourceId + } +} + +@description('The name of the deployed linked service.') +output name string = linkedService.name + +@description('The resource ID of the deployed linked service.') +output resourceId string = linkedService.id + +@description('The resource group where the linked service is deployed.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/automation/automation-account/runbook/main.json b/avm/res/automation/automation-account/runbook/main.json index 4e1b2e83f5..317cf0c2c5 100644 --- a/avm/res/automation/automation-account/runbook/main.json +++ b/avm/res/automation/automation-account/runbook/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16990805873972671446" + "version": "0.29.47.4906", + "templateHash": "11653746330709835761" }, "name": "Automation Account Runbooks", "description": "This module deploys an Azure Automation Account Runbook.", diff --git a/avm/res/automation/automation-account/schedule/main.json b/avm/res/automation/automation-account/schedule/main.json index 9580ab57ca..46c5820b48 100644 --- a/avm/res/automation/automation-account/schedule/main.json +++ b/avm/res/automation/automation-account/schedule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11730927882334055011" + "version": "0.29.47.4906", + "templateHash": "6299933839999486418" }, "name": "Automation Account Schedules", "description": "This module deploys an Azure Automation Account Schedule.", diff --git a/avm/res/automation/automation-account/software-update-configuration/main.json b/avm/res/automation/automation-account/software-update-configuration/main.json index 09946712f1..8f7f4c5ecb 100644 --- a/avm/res/automation/automation-account/software-update-configuration/main.json +++ b/avm/res/automation/automation-account/software-update-configuration/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5046214814278705138" + "version": "0.29.47.4906", + "templateHash": "752305553206238634" }, "name": "Automation Account Software Update Configurations", "description": "This module deploys an Azure Automation Account Software Update Configuration.", diff --git a/avm/res/automation/automation-account/tests/e2e/max/main.test.bicep b/avm/res/automation/automation-account/tests/e2e/max/main.test.bicep index d85ff4796d..a3dfb495d2 100644 --- a/avm/res/automation/automation-account/tests/e2e/max/main.test.bicep +++ b/avm/res/automation/automation-account/tests/e2e/max/main.test.bicep @@ -116,9 +116,13 @@ module testDeployment '../../../main.bicep' = [ ] privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'Webhook' subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId tags: { @@ -128,9 +132,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'Webhook' subnetResourceId: nestedDependencies.outputs.customSubnet2ResourceId tags: { @@ -140,9 +148,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'DSCAndHybridWorker' subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId tags: { @@ -154,11 +166,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'de334944-f952-4273-8ab3-bd523380034c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep index d6182ea498..355d2a8e53 100644 --- a/avm/res/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/automation/automation-account/tests/e2e/waf-aligned/main.test.bicep @@ -103,9 +103,13 @@ module testDeployment '../../../main.bicep' = [ ] privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'Webhook' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -115,9 +119,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'DSCAndHybridWorker' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { diff --git a/avm/res/automation/automation-account/variable/main.json b/avm/res/automation/automation-account/variable/main.json index 0b3a6fd241..70ba3c0c1e 100644 --- a/avm/res/automation/automation-account/variable/main.json +++ b/avm/res/automation/automation-account/variable/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17408217027458282021" + "version": "0.29.47.4906", + "templateHash": "13284693609482301780" }, "name": "Automation Account Variables", "description": "This module deploys an Azure Automation Account Variable.", diff --git a/avm/res/automation/automation-account/version.json b/avm/res/automation/automation-account/version.json index a8eda31021..7e1d3f4157 100644 --- a/avm/res/automation/automation-account/version.json +++ b/avm/res/automation/automation-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/batch/batch-account/README.md b/avm/res/batch/batch-account/README.md index f2d6ddd938..f716ced588 100644 --- a/avm/res/batch/batch-account/README.md +++ b/avm/res/batch/batch-account/README.md @@ -19,8 +19,8 @@ This module deploys a Batch Account. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Batch/batchAccounts` | [2022-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Batch/2022-06-01/batchAccounts) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -245,15 +245,25 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { + name: '9afa4fb3-2157-40db-aebb-039ce73c50ca' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } { principalId: '' principalType: 'ServicePrincipal' @@ -269,9 +279,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'batchAccount' subnetResourceId: '' tags: { @@ -281,9 +295,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'nodeManagement' subnetResourceId: '' tags: { @@ -295,11 +313,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { ] roleAssignments: [ { + name: 'd57821b0-52b3-4a42-9799-533a9cdb7eec' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -405,15 +425,25 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { + "name": "9afa4fb3-2157-40db-aebb-039ce73c50ca", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, { "principalId": "", "principalType": "ServicePrincipal", @@ -429,9 +459,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "batchAccount", "subnetResourceId": "", "tags": { @@ -441,9 +475,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "nodeManagement", "subnetResourceId": "", "tags": { @@ -457,11 +495,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { "roleAssignments": { "value": [ { + "name": "d57821b0-52b3-4a42-9799-533a9cdb7eec", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -529,9 +569,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { poolAllocationMode: 'BatchService' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'batchAccount' subnetResourceId: '' } @@ -597,9 +641,13 @@ module batchAccount 'br/public:avm/res/batch/batch-account:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "batchAccount", "subnetResourceId": "" } @@ -1091,8 +1139,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1282,19 +1329,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1332,6 +1424,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1382,6 +1475,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1444,6 +1544,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1494,6 +1595,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1547,6 +1655,7 @@ Tags of the resource. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the batch account. | +| `privateEndpoints` | array | The private endpoints of the batch account. | | `resourceGroupName` | string | The resource group the batch account was deployed into. | | `resourceId` | string | The resource ID of the batch account. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1557,7 +1666,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/batch/batch-account/main.bicep b/avm/res/batch/batch-account/main.bicep index be0f8f7192..1e63676d03 100644 --- a/avm/res/batch/batch-account/main.bicep +++ b/avm/res/batch/batch-account/main.bicep @@ -116,6 +116,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.batch-batchaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -250,7 +261,7 @@ resource batchAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@ } ] -module batchAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module batchAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-batchAccount-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -291,8 +302,7 @@ module batchAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -304,14 +314,10 @@ module batchAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint ] resource batchAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(batchAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(batchAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -338,6 +344,17 @@ output location string = batchAccount.location @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = batchAccount.?identity.?principalId ?? '' +@description('The private endpoints of the batch account.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: batchAccount_privateEndpoints[i].outputs.name + resourceId: batchAccount_privateEndpoints[i].outputs.resourceId + groupId: batchAccount_privateEndpoints[i].outputs.groupId + customDnsConfig: batchAccount_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: batchAccount_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // ================ // // Definitions // // ================ // @@ -387,6 +404,9 @@ type diagnosticSettingType = { }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -425,11 +445,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/batch/batch-account/main.json b/avm/res/batch/batch-account/main.json index 5c65305670..9a446638ad 100644 --- a/avm/res/batch/batch-account/main.json +++ b/avm/res/batch/batch-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4927537341487621364" + "version": "0.29.47.4906", + "templateHash": "14365584147698866466" }, "name": "Batch Accounts", "description": "This module deploys a Batch Account.", @@ -138,6 +138,13 @@ "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": { @@ -237,21 +244,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -668,6 +698,11 @@ "action": "Allow", "value": "[coalesce(tryGet(tryGet(parameters('networkProfile'), 'nodeManagementAccess'), 'allowedIpRules'), createArray())[copyIndex('nodeManagementAccessNetworkProfileIpRules')]]" } + }, + { + "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'))))]", @@ -814,20 +849,20 @@ "batchAccount_roleAssignments": { "copy": { "name": "batchAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Batch/batchAccounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Batch/batchAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Batch/batchAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "batchAccount" @@ -865,11 +900,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -897,19 +929,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1013,13 +1073,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1053,8 +1113,11 @@ "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." + "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": { @@ -1094,8 +1157,11 @@ "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." + "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": { @@ -1143,6 +1209,29 @@ } }, "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": { @@ -1178,18 +1267,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1245,6 +1327,13 @@ } }, "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')]", @@ -1262,8 +1351,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1281,7 +1370,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1322,27 +1411,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1353,28 +1442,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1382,12 +1495,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1401,27 +1517,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1479,14 +1604,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1531,6 +1670,22 @@ "description": "The principal ID of the system assigned identity." }, "value": "[coalesce(tryGet(tryGet(reference('batchAccount', '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the batch account." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('batchAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('batchAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('batchAccount_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('batchAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('batchAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/batch/batch-account/tests/e2e/max/main.test.bicep b/avm/res/batch/batch-account/tests/e2e/max/main.test.bicep index f0a93b9ac7..c4d73d9a51 100644 --- a/avm/res/batch/batch-account/tests/e2e/max/main.test.bicep +++ b/avm/res/batch/batch-account/tests/e2e/max/main.test.bicep @@ -85,9 +85,13 @@ module testDeployment '../../../main.bicep' = [ poolAllocationMode: 'BatchService' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'batchAccount' subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId tags: { @@ -97,10 +101,16 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '9afa4fb3-2157-40db-aebb-039ce73c50ca' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } { roleDefinitionIdOrName: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', @@ -130,9 +140,13 @@ module testDeployment '../../../main.bicep' = [ ] } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'batchAccount' subnetResourceId: nestedDependencies.outputs.customSubnet2ResourceId tags: { @@ -142,9 +156,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'nodeManagement' subnetResourceId: nestedDependencies.outputs.customSubnet1ResourceId tags: { @@ -169,11 +187,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'd57821b0-52b3-4a42-9799-533a9cdb7eec' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep index 72c3a1a51d..76e945d9cb 100644 --- a/avm/res/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/batch/batch-account/tests/e2e/waf-aligned/main.test.bicep @@ -84,9 +84,13 @@ module testDeployment '../../../main.bicep' = [ poolAllocationMode: 'BatchService' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'batchAccount' subnetResourceId: nestedDependencies.outputs.subnetResourceId } diff --git a/avm/res/batch/batch-account/version.json b/avm/res/batch/batch-account/version.json index e42c3d9e5f..0f81d22abc 100644 --- a/avm/res/batch/batch-account/version.json +++ b/avm/res/batch/batch-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index 13bd7725e1..786def2c2d 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -21,8 +21,8 @@ This module deploys a Redis Cache. | `Microsoft.Cache/redis` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/redis) | | `Microsoft.Cache/redis/linkedServers` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cache/redis/linkedServers) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -188,11 +188,16 @@ module redis 'br/public:avm/res/cache/redis:' = { minimumTlsVersion: '1.2' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { + name: '8d6043f5-8a22-447f-bc31-23d23e09de6c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' @@ -216,20 +221,26 @@ module redis 'br/public:avm/res/cache/redis:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] redisVersion: '6' roleAssignments: [ { + name: 'f20e5c94-a697-421e-8768-d576399dbd87' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -317,11 +328,16 @@ module redis 'br/public:avm/res/cache/redis:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { + "name": "8d6043f5-8a22-447f-bc31-23d23e09de6c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" @@ -345,9 +361,13 @@ module redis 'br/public:avm/res/cache/redis:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -358,11 +378,13 @@ module redis 'br/public:avm/res/cache/redis:' = { "roleAssignments": { "value": [ { + "name": "f20e5c94-a697-421e-8768-d576399dbd87", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -551,9 +573,13 @@ module redis 'br/public:avm/res/cache/redis:' = { minimumTlsVersion: '1.2' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -640,9 +666,13 @@ module redis 'br/public:avm/res/cache/redis:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -1040,8 +1070,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1225,19 +1254,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1275,6 +1349,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1325,6 +1400,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1433,6 +1515,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1483,6 +1566,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1586,6 +1676,7 @@ If the zoneRedundant parameter is true, replicas will be provisioned in the avai | `hostName` | string | Redis hostname. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the Redis Cache. | +| `privateEndpoints` | array | The private endpoints of the Redis Cache. | | `resourceGroupName` | string | The name of the resource group the Redis Cache was created in. | | `resourceId` | string | The resource ID of the Redis Cache. | | `sslPort` | int | Redis SSL port. | @@ -1598,7 +1689,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Notes diff --git a/avm/res/cache/redis/linked-servers/main.json b/avm/res/cache/redis/linked-servers/main.json index 5834cb3527..67532e2795 100644 --- a/avm/res/cache/redis/linked-servers/main.json +++ b/avm/res/cache/redis/linked-servers/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16388556183951135369" + "version": "0.29.47.4906", + "templateHash": "6993324639761594928" }, "name": "Redis Cache Linked Servers", "description": "This module connects a primary and secondary Redis Cache together for geo-replication.", diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index 97973ae0fa..778d48e0f2 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -145,6 +145,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.cache-redis.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -233,14 +244,10 @@ resource redis_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05 ] resource redis_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(redis.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(redis.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -252,7 +259,7 @@ resource redis_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04- } ] -module redis_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module redis_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-redis-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -293,8 +300,7 @@ module redis_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -340,6 +346,17 @@ output systemAssignedMIPrincipalId string = redis.?identity.?principalId ?? '' @description('The location the resource was deployed into.') output location string = redis.location +@description('The private endpoints of the Redis Cache.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: redis_privateEndpoints[i].outputs.name + resourceId: redis_privateEndpoints[i].outputs.resourceId + groupId: redis_privateEndpoints[i].outputs.groupId + customDnsConfig: redis_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: redis_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -376,11 +393,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @@ -439,6 +465,9 @@ type privateEndpointType = { }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index 94b1435f91..ce0660ef3a 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2290807334954408689" + "version": "0.29.47.4906", + "templateHash": "16679190944916422791" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache.", @@ -100,21 +100,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -263,6 +286,13 @@ "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": { @@ -655,6 +685,13 @@ } }, "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)))))]" + } + ], "availabilityZones": "[if(equals(parameters('skuName'), 'Premium'), if(parameters('zoneRedundant'), if(not(empty(parameters('zones'))), parameters('zones'), pickZones('Microsoft.Cache', 'redis', parameters('location'), 3)), createArray()), createArray())]", "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())]", @@ -773,20 +810,20 @@ "redis_roleAssignments": { "copy": { "name": "redis_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Cache/redis/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Cache/redis', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Cache/redis', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "redis" @@ -824,11 +861,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -856,19 +890,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -972,13 +1034,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1012,8 +1074,11 @@ "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." + "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": { @@ -1053,8 +1118,11 @@ "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." + "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": { @@ -1102,6 +1170,29 @@ } }, "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": { @@ -1137,18 +1228,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1204,6 +1288,13 @@ } }, "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')]", @@ -1221,8 +1312,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1240,7 +1331,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1281,27 +1372,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1312,28 +1403,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1341,12 +1456,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1360,27 +1478,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1438,14 +1565,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1485,8 +1626,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16388556183951135369" + "version": "0.29.47.4906", + "templateHash": "6993324639761594928" }, "name": "Redis Cache Linked Servers", "description": "This module connects a primary and secondary Redis Cache together for geo-replication.", @@ -1642,6 +1783,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('redis', '2024-03-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Redis Cache." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('redis_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/cache/redis/tests/e2e/max/main.test.bicep b/avm/res/cache/redis/tests/e2e/max/main.test.bicep index fc6309abb0..2436361af9 100644 --- a/avm/res/cache/redis/tests/e2e/max/main.test.bicep +++ b/avm/res/cache/redis/tests/e2e/max/main.test.bicep @@ -92,12 +92,17 @@ module testDeployment '../../../main.bicep' = [ zones: [1, 2] privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId roleAssignments: [ { + name: '8d6043f5-8a22-447f-bc31-23d23e09de6c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -123,9 +128,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] @@ -140,11 +149,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'f20e5c94-a697-421e-8768-d576399dbd87' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep index 6e86ccb362..d9fa9d90a5 100644 --- a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep @@ -91,9 +91,13 @@ module testDeployment '../../../main.bicep' = [ zones: [1, 2, 3] privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' diff --git a/avm/res/cache/redis/version.json b/avm/res/cache/redis/version.json index 96236a61ba..e42c3d9e5f 100644 --- a/avm/res/cache/redis/version.json +++ b/avm/res/cache/redis/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.6", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/README.md b/avm/res/cdn/profile/README.md index 45ce097300..538adef706 100644 --- a/avm/res/cdn/profile/README.md +++ b/avm/res/cdn/profile/README.md @@ -343,11 +343,13 @@ module profile 'br/public:avm/res/cdn/profile:' = { originResponseTimeoutSeconds: 60 roleAssignments: [ { + name: '50362c78-6910-43c3-8639-9cae123943bb' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -429,11 +431,13 @@ module profile 'br/public:avm/res/cdn/profile:' = { "roleAssignments": { "value": [ { + "name": "50362c78-6910-43c3-8639-9cae123943bb", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -754,6 +758,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -804,6 +809,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -849,11 +861,14 @@ Endpoint tags. | Output | Type | Description | | :-- | :-- | :-- | +| `endpointId` | string | The resource ID of the CDN profile endpoint. | +| `endpointName` | string | The name of the CDN profile endpoint. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the CDN profile. | | `profileType` | string | The type of the CDN profile. | | `resourceGroupName` | string | The resource group where the CDN profile is deployed. | | `resourceId` | string | The resource ID of the CDN profile. | +| `uri` | string | The uri of the CDN profile endpoint. | ## Cross-referenced modules diff --git a/avm/res/cdn/profile/afdEndpoint/README.md b/avm/res/cdn/profile/afdEndpoint/README.md index 1824848d10..8534159c47 100644 --- a/avm/res/cdn/profile/afdEndpoint/README.md +++ b/avm/res/cdn/profile/afdEndpoint/README.md @@ -118,6 +118,7 @@ The tags of the AFD Endpoint. | `name` | string | The name of the AFD Endpoint. | | `resourceGroupName` | string | The name of the resource group the endpoint was created in. | | `resourceId` | string | The resource id of the AFD Endpoint. | +| `routes` | array | The list of routes assigned to the AFD endpoint. | ## Cross-referenced modules diff --git a/avm/res/cdn/profile/afdEndpoint/main.bicep b/avm/res/cdn/profile/afdEndpoint/main.bicep index f58b77ad8c..a280fcf07f 100644 --- a/avm/res/cdn/profile/afdEndpoint/main.bicep +++ b/avm/res/cdn/profile/afdEndpoint/main.bicep @@ -81,3 +81,6 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = afdEndpoint.location + +@description('The list of routes assigned to the AFD endpoint.') +output routes array = routes ?? [] diff --git a/avm/res/cdn/profile/afdEndpoint/main.json b/avm/res/cdn/profile/afdEndpoint/main.json index 925daf8da7..1f55d5cd6e 100644 --- a/avm/res/cdn/profile/afdEndpoint/main.json +++ b/avm/res/cdn/profile/afdEndpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1139997023996590437" + "version": "0.29.47.4906", + "templateHash": "3384292547879688658" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -156,8 +156,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5770374289363120600" + "version": "0.29.47.4906", + "templateHash": "18002678880456924020" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", @@ -431,6 +431,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('afdEndpoint', '2023-05-01', 'full').location]" + }, + "routes": { + "type": "array", + "metadata": { + "description": "The list of routes assigned to the AFD endpoint." + }, + "value": "[coalesce(parameters('routes'), createArray())]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/afdEndpoint/route/main.json b/avm/res/cdn/profile/afdEndpoint/route/main.json index 5f6294f050..6a415af662 100644 --- a/avm/res/cdn/profile/afdEndpoint/route/main.json +++ b/avm/res/cdn/profile/afdEndpoint/route/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5770374289363120600" + "version": "0.29.47.4906", + "templateHash": "18002678880456924020" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", @@ -244,4 +244,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/customdomain/main.json b/avm/res/cdn/profile/customdomain/main.json index 8966e5eb46..dd0a43d181 100644 --- a/avm/res/cdn/profile/customdomain/main.json +++ b/avm/res/cdn/profile/customdomain/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15721665305636481516" + "version": "0.29.47.4906", + "templateHash": "15211066835326278081" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", @@ -121,4 +121,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/endpoint/README.md b/avm/res/cdn/profile/endpoint/README.md index ac8d86345a..1044e2e951 100644 --- a/avm/res/cdn/profile/endpoint/README.md +++ b/avm/res/cdn/profile/endpoint/README.md @@ -85,6 +85,7 @@ Endpoint tags. | `name` | string | The name of the endpoint. | | `resourceGroupName` | string | The name of the resource group the endpoint was created in. | | `resourceId` | string | The resource ID of the endpoint. | +| `uri` | string | The uri of the endpoint. | ## Cross-referenced modules diff --git a/avm/res/cdn/profile/endpoint/main.bicep b/avm/res/cdn/profile/endpoint/main.bicep index 2379bba4d0..6ce0fd6de9 100644 --- a/avm/res/cdn/profile/endpoint/main.bicep +++ b/avm/res/cdn/profile/endpoint/main.bicep @@ -64,3 +64,6 @@ output location string = endpoint.location @description('The properties of the endpoint.') output endpointProperties object = endpoint.properties + +@description('The uri of the endpoint.') +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/avm/res/cdn/profile/endpoint/main.json b/avm/res/cdn/profile/endpoint/main.json index 51a7779ed9..2fa89e8711 100644 --- a/avm/res/cdn/profile/endpoint/main.json +++ b/avm/res/cdn/profile/endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2906172435071993445" + "version": "0.29.47.4906", + "templateHash": "6371656015674390162" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -125,8 +125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3665403791951260301" + "version": "0.29.47.4906", + "templateHash": "11976988406992266750" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", @@ -321,6 +321,13 @@ "description": "The properties of the endpoint." }, "value": "[reference('endpoint')]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The uri of the endpoint." + }, + "value": "[format('https://{0}', reference('endpoint').hostName)]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/endpoint/origin/main.json b/avm/res/cdn/profile/endpoint/origin/main.json index 3d2af22841..13a2f8b35d 100644 --- a/avm/res/cdn/profile/endpoint/origin/main.json +++ b/avm/res/cdn/profile/endpoint/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3665403791951260301" + "version": "0.29.47.4906", + "templateHash": "11976988406992266750" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", @@ -158,4 +158,4 @@ "value": "[reference('endpoint', '2021-06-01', 'full').location]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/main.bicep b/avm/res/cdn/profile/main.bicep index 1e951d3c75..8359ab6450 100644 --- a/avm/res/cdn/profile/main.bicep +++ b/avm/res/cdn/profile/main.bicep @@ -91,6 +91,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.cdn-profile.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -134,14 +145,10 @@ resource profile_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource profile_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(profile.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(profile.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -259,6 +266,15 @@ output profileType string = profile.type @description('The location the resource was deployed into.') output location string = profile.location +@description('The name of the CDN profile endpoint.') +output endpointName string = !empty(endpointProperties) ? profile_endpoint.outputs.name : '' + +@description('The resource ID of the CDN profile endpoint.') +output endpointId string = !empty(endpointProperties) ? profile_endpoint.outputs.resourceId : '' + +@description('The uri of the CDN profile endpoint.') +output uri string = !empty(endpointProperties) ? profile_endpoint.outputs.uri : '' + // =============== // // Definitions // // =============== // @@ -272,6 +288,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/cdn/profile/main.json b/avm/res/cdn/profile/main.json index 10d4e53686..59fc991ac2 100644 --- a/avm/res/cdn/profile/main.json +++ b/avm/res/cdn/profile/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1004296684432790978" + "version": "0.29.47.4906", + "templateHash": "10527581582065544057" }, "name": "CDN Profiles", "description": "This module deploys a CDN Profile.", @@ -43,6 +43,13 @@ "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": { @@ -223,6 +230,13 @@ } }, "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": { "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", @@ -286,20 +300,20 @@ "profile_roleAssignments": { "copy": { "name": "profile_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Cdn/profiles/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Cdn/profiles', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Cdn/profiles', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "profile" @@ -336,8 +350,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "646975231992417020" + "version": "0.29.47.4906", + "templateHash": "6371656015674390162" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -456,8 +470,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3976201598135370414" + "version": "0.29.47.4906", + "templateHash": "11976988406992266750" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", @@ -652,6 +666,13 @@ "description": "The properties of the endpoint." }, "value": "[reference('endpoint')]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The uri of the endpoint." + }, + "value": "[format('https://{0}', reference('endpoint').hostName)]" } } } @@ -702,8 +723,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11180937853362920611" + "version": "0.29.47.4906", + "templateHash": "533126228291817357" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", @@ -851,8 +872,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16920601274318465194" + "version": "0.29.47.4906", + "templateHash": "15211066835326278081" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", @@ -1018,8 +1039,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "767830983281905975" + "version": "0.29.47.4906", + "templateHash": "1182415535491789973" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -1155,8 +1176,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16416786424096977239" + "version": "0.29.47.4906", + "templateHash": "14493731512795008787" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", @@ -1381,8 +1402,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6477030276001558789" + "version": "0.29.47.4906", + "templateHash": "9792708426765797662" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -1467,8 +1488,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13457744126513087238" + "version": "0.29.47.4906", + "templateHash": "12904222825428666192" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", @@ -1655,8 +1676,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2563478312676565785" + "version": "0.29.47.4906", + "templateHash": "3384292547879688658" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -1806,8 +1827,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4116565038484106851" + "version": "0.29.47.4906", + "templateHash": "18002678880456924020" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", @@ -2081,6 +2102,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('afdEndpoint', '2023-05-01', 'full').location]" + }, + "routes": { + "type": "array", + "metadata": { + "description": "The list of routes assigned to the AFD endpoint." + }, + "value": "[coalesce(parameters('routes'), createArray())]" } } } @@ -2128,6 +2156,27 @@ "description": "The location the resource was deployed into." }, "value": "[reference('profile', '2023-05-01', 'full').location]" + }, + "endpointName": { + "type": "string", + "metadata": { + "description": "The name of the CDN profile endpoint." + }, + "value": "[if(not(empty(parameters('endpointProperties'))), reference('profile_endpoint').outputs.name.value, '')]" + }, + "endpointId": { + "type": "string", + "metadata": { + "description": "The resource ID of the CDN profile endpoint." + }, + "value": "[if(not(empty(parameters('endpointProperties'))), reference('profile_endpoint').outputs.resourceId.value, '')]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The uri of the CDN profile endpoint." + }, + "value": "[if(not(empty(parameters('endpointProperties'))), reference('profile_endpoint').outputs.uri.value, '')]" } } } \ No newline at end of file diff --git a/avm/res/cdn/profile/origingroup/main.json b/avm/res/cdn/profile/origingroup/main.json index 13a07fe56c..9a388bc48e 100644 --- a/avm/res/cdn/profile/origingroup/main.json +++ b/avm/res/cdn/profile/origingroup/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12438540618132459307" + "version": "0.29.47.4906", + "templateHash": "1182415535491789973" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -142,8 +142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8566106020570825253" + "version": "0.29.47.4906", + "templateHash": "14493731512795008787" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", @@ -331,4 +331,4 @@ "value": "[reference('profile', '2023-05-01', 'full').location]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/origingroup/origin/main.json b/avm/res/cdn/profile/origingroup/origin/main.json index 6c1cdb4ebe..56306ce135 100644 --- a/avm/res/cdn/profile/origingroup/origin/main.json +++ b/avm/res/cdn/profile/origingroup/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8566106020570825253" + "version": "0.29.47.4906", + "templateHash": "14493731512795008787" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", @@ -157,4 +157,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/ruleset/main.json b/avm/res/cdn/profile/ruleset/main.json index 7bdf9944da..47ff335b1c 100644 --- a/avm/res/cdn/profile/ruleset/main.json +++ b/avm/res/cdn/profile/ruleset/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "5891069247146856543" + "version": "0.29.47.4906", + "templateHash": "9792708426765797662" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -91,8 +91,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4690708071413750601" + "version": "0.29.47.4906", + "templateHash": "12904222825428666192" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", @@ -230,4 +230,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/ruleset/rule/main.json b/avm/res/cdn/profile/ruleset/rule/main.json index 14828f7139..bb37297681 100644 --- a/avm/res/cdn/profile/ruleset/rule/main.json +++ b/avm/res/cdn/profile/ruleset/rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4690708071413750601" + "version": "0.29.47.4906", + "templateHash": "12904222825428666192" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", @@ -117,4 +117,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/secret/main.json b/avm/res/cdn/profile/secret/main.json index dd68ba9f77..a23afb02a0 100644 --- a/avm/res/cdn/profile/secret/main.json +++ b/avm/res/cdn/profile/secret/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "364931243138434002" + "version": "0.29.47.4906", + "templateHash": "533126228291817357" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", @@ -99,4 +99,4 @@ "value": "[resourceGroup().name]" } } -} +} \ No newline at end of file diff --git a/avm/res/cdn/profile/tests/e2e/max/main.test.bicep b/avm/res/cdn/profile/tests/e2e/max/main.test.bicep index 4a66b858fa..8ed5b744c1 100644 --- a/avm/res/cdn/profile/tests/e2e/max/main.test.bicep +++ b/avm/res/cdn/profile/tests/e2e/max/main.test.bicep @@ -91,11 +91,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '50362c78-6910-43c3-8639-9cae123943bb' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/cdn/profile/version.json b/avm/res/cdn/profile/version.json index b3d560b1ad..3f863a2bec 100644 --- a/avm/res/cdn/profile/version.json +++ b/avm/res/cdn/profile/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md index 334da0443c..6add4f02af 100644 --- a/avm/res/cognitive-services/account/README.md +++ b/avm/res/cognitive-services/account/README.md @@ -20,8 +20,8 @@ This module deploys a Cognitive Service. | `Microsoft.CognitiveServices/accounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts) | | `Microsoft.CognitiveServices/accounts/deployments` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts/deployments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -31,23 +31,129 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/cognitive-services/account:`. -- [Using `deployments` in parameter set](#example-1-using-deployments-in-parameter-set) -- [Using only defaults](#example-2-using-only-defaults) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [As Speech Service](#example-4-as-speech-service) -- [Using Customer-Managed-Keys with System-Assigned identity](#example-5-using-customer-managed-keys-with-system-assigned-identity) -- [Using Customer-Managed-Keys with User-Assigned identity](#example-6-using-customer-managed-keys-with-user-assigned-identity) -- [WAF-aligned](#example-7-waf-aligned) +- [Using `AIServices` with `deployments` in parameter set and private endpoints](#example-1-using-aiservices-with-deployments-in-parameter-set-and-private-endpoints) +- [Using `AIServices` with `deployments` in parameter set](#example-2-using-aiservices-with-deployments-in-parameter-set) +- [Using only defaults](#example-3-using-only-defaults) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [Using `OpenAI` and `deployments` in parameter set with private endpoint](#example-5-using-openai-and-deployments-in-parameter-set-with-private-endpoint) +- [As Speech Service](#example-6-as-speech-service) +- [Using Customer-Managed-Keys with System-Assigned identity](#example-7-using-customer-managed-keys-with-system-assigned-identity) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-8-using-customer-managed-keys-with-user-assigned-identity) +- [WAF-aligned](#example-9-waf-aligned) -### Example 1: _Using `deployments` in parameter set_ +### Example 1: _Using `AIServices` with `deployments` in parameter set and private endpoints_ -This instance deploys the module with the AI model deployment feature.' +This instance deploys the module with the AI model deployment feature and private endpoint. -Note, this test is temporarily disabled as it needs to be enabled on the subscription. -As we don't want other contributions from being blocked by this, we disabled the test for now / rely on a manual execution outside the CI environemnt -You can find more information here: https://learn.microsoft.com/en-us/legal/cognitive-services/openai/limited-access -And register here: https://aka.ms/oai/access +

+ +via Bicep module + +```bicep +module account 'br/public:avm/res/cognitive-services/account:' = { + name: 'accountDeployment' + params: { + // Required parameters + kind: 'AIServices' + name: 'csadp003' + // Non-required parameters + customSubDomainName: 'xcsadpai' + deployments: [ + { + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + name: 'gpt-35-turbo' + sku: { + capacity: 10 + name: 'Standard' + } + } + ] + location: '' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + '' + ] + subnetResourceId: '' + } + ] + publicNetworkAccess: 'Disabled' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "AIServices" + }, + "name": { + "value": "csadp003" + }, + // Non-required parameters + "customSubDomainName": { + "value": "xcsadpai" + }, + "deployments": { + "value": [ + { + "model": { + "format": "OpenAI", + "name": "gpt-35-turbo", + "version": "0301" + }, + "name": "gpt-35-turbo", + "sku": { + "capacity": 10, + "name": "Standard" + } + } + ] + }, + "location": { + "value": "" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "", + "" + ], + "subnetResourceId": "" + } + ] + }, + "publicNetworkAccess": { + "value": "Disabled" + } + } +} +``` + +
+

+ +### Example 2: _Using `AIServices` with `deployments` in parameter set_ + +This instance deploys the module with the AI model deployment feature.

@@ -131,7 +237,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 2: _Using only defaults_ +### Example 3: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -183,7 +289,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 3: _Using large parameter set_ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -288,11 +394,13 @@ module account 'br/public:avm/res/cognitive-services/account:' = { publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: 'db64fe2f-3995-4ae0-86ef-97511d5b84e3' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -438,11 +546,13 @@ module account 'br/public:avm/res/cognitive-services/account:' = { "roleAssignments": { "value": [ { + "name": "db64fe2f-3995-4ae0-86ef-97511d5b84e3", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -471,7 +581,115 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 4: _As Speech Service_ +### Example 5: _Using `OpenAI` and `deployments` in parameter set with private endpoint_ + +This instance deploys the module with the AI model deployment feature and private endpoint. + + +

+ +via Bicep module + +```bicep +module account 'br/public:avm/res/cognitive-services/account:' = { + name: 'accountDeployment' + params: { + // Required parameters + kind: 'OpenAI' + name: 'csoai002' + // Non-required parameters + customSubDomainName: 'xcsoaiai' + deployments: [ + { + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + name: 'gpt-35-turbo' + sku: { + capacity: 10 + name: 'Standard' + } + } + ] + location: '' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + subnetResourceId: '' + } + ] + publicNetworkAccess: 'Disabled' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "OpenAI" + }, + "name": { + "value": "csoai002" + }, + // Non-required parameters + "customSubDomainName": { + "value": "xcsoaiai" + }, + "deployments": { + "value": [ + { + "model": { + "format": "OpenAI", + "name": "gpt-35-turbo", + "version": "0301" + }, + "name": "gpt-35-turbo", + "sku": { + "capacity": 10, + "name": "Standard" + } + } + ] + }, + "location": { + "value": "" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "subnetResourceId": "" + } + ] + }, + "publicNetworkAccess": { + "value": "Disabled" + } + } +} +``` + +
+

+ +### Example 6: _As Speech Service_ This instance deploys the module as a Speech Service. @@ -585,7 +803,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 5: _Using Customer-Managed-Keys with System-Assigned identity_ +### Example 7: _Using Customer-Managed-Keys with System-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret. @@ -667,7 +885,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 6: _Using Customer-Managed-Keys with User-Assigned identity_ +### Example 8: _Using Customer-Managed-Keys with User-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. @@ -755,7 +973,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 7: _WAF-aligned_ +### Example 9: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -1668,6 +1886,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1718,6 +1937,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1755,11 +1981,9 @@ Whether or not public network access is allowed for this resource. For security - Required: No - Type: string -- Default: `''` - Allowed: ```Bicep [ - '' 'Disabled' 'Enabled' ] @@ -1803,6 +2027,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1853,6 +2078,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1920,6 +2152,7 @@ The storage accounts for this resource. | Output | Type | Description | | :-- | :-- | :-- | | `endpoint` | string | The service endpoint of the cognitive services account. | +| `endpoints` | | All endpoints available for the cognitive services account, types depends on the cognitive service kind. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the cognitive services account. | | `resourceGroupName` | string | The resource group the cognitive services account was deployed into. | @@ -1932,7 +2165,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Data Collection diff --git a/avm/res/cognitive-services/account/main.bicep b/avm/res/cognitive-services/account/main.bicep index 01b7122fb2..0080ac6d3a 100644 --- a/avm/res/cognitive-services/account/main.bicep +++ b/avm/res/cognitive-services/account/main.bicep @@ -64,11 +64,10 @@ param diagnosticSettings diagnosticSettingType @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 = '' +param publicNetworkAccess string? @description('Conditional. Subdomain name used for token-based authentication. Required if \'networkAcls\' or \'privateEndpoints\' are set.') param customSubDomainName string? @@ -248,6 +247,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #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)}' @@ -305,7 +315,7 @@ resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' = { ipRules: networkAcls.?ipRules ?? [] } : null - publicNetworkAccess: !empty(publicNetworkAccess) + publicNetworkAccess: publicNetworkAccess != null ? publicNetworkAccess : (!empty(networkAcls) ? 'Enabled' : 'Disabled') allowedFqdnList: allowedFqdnList @@ -389,7 +399,7 @@ resource cognitiveService_diagnosticSettings 'Microsoft.Insights/diagnosticSetti } ] -module cognitiveService_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module cognitiveService_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-cognitiveService-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -443,14 +453,10 @@ module cognitiveService_privateEndpoints 'br/public:avm/res/network/private-endp ] resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(cognitiveService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(cognitiveService.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -474,6 +480,9 @@ output resourceGroupName string = resourceGroup().name @description('The service endpoint of the cognitive services account.') output endpoint string = cognitiveService.properties.endpoint +@description('All endpoints available for the cognitive services account, types depends on the cognitive service kind.') +output endpoints endpointsType = cognitiveService.properties.endpoints + @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = cognitiveService.?identity.?principalId ?? '' @@ -529,6 +538,9 @@ type diagnosticSettingType = { }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -687,3 +699,10 @@ type deploymentsType = { @description('Optional. The name of RAI policy.') raiPolicyName: string? }[]? + +type endpointsType = { + @description('Type of the endpoint.') + name: string? + @description('The endpoint URI.') + endpoint: string? +}? diff --git a/avm/res/cognitive-services/account/main.json b/avm/res/cognitive-services/account/main.json index b5bcc078ec..2767bbb995 100644 --- a/avm/res/cognitive-services/account/main.json +++ b/avm/res/cognitive-services/account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7966489832259464647" + "version": "0.29.47.4906", + "templateHash": "14735378599748380229" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service.", @@ -138,6 +138,13 @@ "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": { @@ -546,6 +553,26 @@ } }, "nullable": true + }, + "endpointsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "nullable": true } }, "parameters": { @@ -628,9 +655,8 @@ }, "publicNetworkAccess": { "type": "string", - "defaultValue": "", + "nullable": true, "allowedValues": [ - "", "Enabled", "Disabled" ], @@ -760,6 +786,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -859,7 +892,7 @@ "properties": { "customSubDomainName": "[parameters('customSubDomainName')]", "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", - "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "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')]", @@ -952,20 +985,20 @@ "cognitiveService_roleAssignments": { "copy": { "name": "cognitiveService_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1035,8 +1068,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1048,6 +1081,13 @@ "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": { @@ -1151,13 +1191,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1191,8 +1231,11 @@ "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." + "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": { @@ -1232,8 +1275,11 @@ "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." + "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": { @@ -1383,6 +1429,13 @@ } }, "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')]", @@ -1400,8 +1453,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1419,7 +1472,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1460,20 +1513,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1506,8 +1559,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1553,7 +1606,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -1617,14 +1670,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1663,6 +1723,13 @@ }, "value": "[reference('cognitiveService').endpoint]" }, + "endpoints": { + "$ref": "#/definitions/endpointsType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, "systemAssignedMIPrincipalId": { "type": "string", "metadata": { diff --git a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/dependencies.bicep new file mode 100644 index 0000000000..7ce71d5a43 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/dependencies.bicep @@ -0,0 +1,87 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.cognitiveservices.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource privateDNSZoneOpenAI 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.openai.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The resource ID of the created Private DNS zone for OpenAI.') +output privateDNSZoneOpenAIResourceId string = privateDNSZoneOpenAI.id diff --git a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/main.test.bicep new file mode 100644 index 0000000000..fda05f65f1 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment-private/main.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +metadata name = 'Using `AIServices` with `deployments` in parameter set and private endpoints' +metadata description = 'This instance deploys the module with the AI model deployment feature and private endpoint.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csadp' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: if (true == false) { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}-ai' + params: { + name: '${namePrefix}${serviceShort}003' + kind: 'AIServices' + location: resourceLocation + customSubDomainName: '${namePrefix}x${serviceShort}ai' + deployments: [ + { + name: 'gpt-35-turbo' + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + sku: { + name: 'Standard' + capacity: 10 + } + } + ] + publicNetworkAccess: 'Disabled' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + nestedDependencies.outputs.privateDNSZoneOpenAIResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + } + } +] diff --git a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep index 60d04523d3..50301f23e5 100644 --- a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep @@ -1,14 +1,7 @@ targetScope = 'subscription' -metadata name = 'Using `deployments` in parameter set' -metadata description = ''' -This instance deploys the module with the AI model deployment feature.' - -Note, this test is temporarily disabled as it needs to be enabled on the subscription. -As we don't want other contributions from being blocked by this, we disabled the test for now / rely on a manual execution outside the CI environemnt -You can find more information here: https://learn.microsoft.com/en-us/legal/cognitive-services/openai/limited-access -And register here: https://aka.ms/oai/access -''' +metadata name = 'Using `AIServices` with `deployments` in parameter set' +metadata description = 'This instance deploys the module with the AI model deployment feature.' // ========== // // Parameters // @@ -43,7 +36,7 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { // ============== // module testDeployment '../../../main.bicep' = [ - for iteration in ['init', 'idem']: if (true == false) { + for iteration in ['init', 'idem']: { scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}-ai' params: { diff --git a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/warning.tests.ps1 b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/warning.tests.ps1 deleted file mode 100644 index 21deb1f6a3..0000000000 --- a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/warning.tests.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -###################################### -## Additional post-deployment tests ## -###################################### -## -## You can add any custom post-deployment validation tests you want here, or add them spread accross multiple test files in the test case folder. -## -########################### - -param ( - [Parameter(Mandatory = $false)] - [hashtable] $TestInputData = @{} -) - -Describe 'Warning' { - - It 'Disabled test' { - - Write-Output @{ - Warning = "Note, the OpenAI-Deployments test is temporarily disabled as it needs to be enabled on the subscription.
As we don't want other contributions from being blocked by this, we disabled the test for now / rely on a manual execution outside the CI environemnt. For more information please review the [offical docs](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/limited-access) and or register [here](https://aka.ms/oai/access" - - } - } -} - diff --git a/avm/res/cognitive-services/account/tests/e2e/max/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/max/main.test.bicep index 1ba0cf8985..86f9529a0f 100644 --- a/avm/res/cognitive-services/account/tests/e2e/max/main.test.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/max/main.test.bicep @@ -111,11 +111,13 @@ module testDeployment '../../../main.bicep' = [ publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: 'db64fe2f-3995-4ae0-86ef-97511d5b84e3' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/cognitive-services/account/tests/e2e/openai-private/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/openai-private/dependencies.bicep new file mode 100644 index 0000000000..129b6f6579 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/openai-private/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.cognitiveservices.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/cognitive-services/account/tests/e2e/openai-private/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/openai-private/main.test.bicep new file mode 100644 index 0000000000..046f81ead1 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/openai-private/main.test.bicep @@ -0,0 +1,83 @@ +targetScope = 'subscription' + +metadata name = 'Using `OpenAI` and `deployments` in parameter set with private endpoint' +metadata description = 'This instance deploys the module with the AI model deployment feature and private endpoint.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csoai' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}-oai' + params: { + name: '${namePrefix}${serviceShort}002' + kind: 'OpenAI' + location: resourceLocation + customSubDomainName: '${namePrefix}x${serviceShort}ai' + deployments: [ + { + name: 'gpt-35-turbo' + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + sku: { + name: 'Standard' + capacity: 10 + } + } + ] + publicNetworkAccess: 'Disabled' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + } + } +] diff --git a/avm/res/cognitive-services/account/version.json b/avm/res/cognitive-services/account/version.json index a8eda31021..7e1d3f4157 100644 --- a/avm/res/cognitive-services/account/version.json +++ b/avm/res/cognitive-services/account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/communication/communication-service/README.md b/avm/res/communication/communication-service/README.md index 5370ac81fb..5f08653edb 100644 --- a/avm/res/communication/communication-service/README.md +++ b/avm/res/communication/communication-service/README.md @@ -131,11 +131,13 @@ module communicationService 'br/public:avm/res/communication/communication-servi } roleAssignments: [ { + name: '9237b909-e8fb-4bb8-8194-34aae537cee2' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -216,11 +218,13 @@ module communicationService 'br/public:avm/res/communication/communication-servi "roleAssignments": { "value": [ { + "name": "9237b909-e8fb-4bb8-8194-34aae537cee2", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -620,6 +624,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -670,6 +675,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/communication/communication-service/main.bicep b/avm/res/communication/communication-service/main.bicep index 6a5fa529b2..d96dfe2ca6 100644 --- a/avm/res/communication/communication-service/main.bicep +++ b/avm/res/communication/communication-service/main.bicep @@ -63,6 +63,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -138,14 +149,14 @@ resource communicationService_diagnosticSettings 'Microsoft.Insights/diagnosticS ] resource communicationService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(communicationService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + communicationService.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -197,6 +208,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/communication/communication-service/main.json b/avm/res/communication/communication-service/main.json index 31c50ef7d7..f7c9ec4dba 100644 --- a/avm/res/communication/communication-service/main.json +++ b/avm/res/communication/communication-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16843357491453372043" + "version": "0.29.47.4906", + "templateHash": "12710453655201949687" }, "name": "Communication Services", "description": "This module deploys a Communication Service", @@ -66,6 +66,13 @@ "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": { @@ -320,6 +327,13 @@ } }, "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": { @@ -421,20 +435,20 @@ "communicationService_roleAssignments": { "copy": { "name": "communicationService_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Communication/communicationServices/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Communication/communicationServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Communication/communicationServices', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "communicationService" diff --git a/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep b/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep index 0fbc81cab2..07f5039be6 100644 --- a/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep +++ b/avm/res/communication/communication-service/tests/e2e/max/main.test.bicep @@ -77,11 +77,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '9237b909-e8fb-4bb8-8194-34aae537cee2' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/communication/communication-service/version.json b/avm/res/communication/communication-service/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/communication/communication-service/version.json +++ b/avm/res/communication/communication-service/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/communication/email-service/README.md b/avm/res/communication/email-service/README.md index 92df127665..530c6358f9 100644 --- a/avm/res/communication/email-service/README.md +++ b/avm/res/communication/email-service/README.md @@ -112,6 +112,7 @@ module emailService 'br/public:avm/res/communication/email-service:' = name: 'AzureManagedDomain' roleAssignments: [ { + name: '1a441bec-9c57-49d1-9a83-b7fd62901413' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' @@ -152,11 +153,13 @@ module emailService 'br/public:avm/res/communication/email-service:' = } roleAssignments: [ { + name: 'bdfa5270-8a55-466d-90d0-b5e96a90fadc' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -207,6 +210,7 @@ module emailService 'br/public:avm/res/communication/email-service:' = "name": "AzureManagedDomain", "roleAssignments": [ { + "name": "1a441bec-9c57-49d1-9a83-b7fd62901413", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" @@ -253,11 +257,13 @@ module emailService 'br/public:avm/res/communication/email-service:' = "roleAssignments": { "value": [ { + "name": "bdfa5270-8a55-466d-90d0-b5e96a90fadc", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -463,6 +469,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -513,6 +520,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/communication/email-service/domain/README.md b/avm/res/communication/email-service/domain/README.md index 47e8dd60c2..2079d72073 100644 --- a/avm/res/communication/email-service/domain/README.md +++ b/avm/res/communication/email-service/domain/README.md @@ -141,6 +141,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -191,6 +192,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/communication/email-service/domain/main.bicep b/avm/res/communication/email-service/domain/main.bicep index ebb7598e32..a274b4d64d 100644 --- a/avm/res/communication/email-service/domain/main.bicep +++ b/avm/res/communication/email-service/domain/main.bicep @@ -54,6 +54,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -98,14 +109,10 @@ resource domain_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(loc } resource domain_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(domain.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(domain.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -143,6 +150,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/communication/email-service/domain/main.json b/avm/res/communication/email-service/domain/main.json index bf95f4c8ac..100173ad62 100644 --- a/avm/res/communication/email-service/domain/main.json +++ b/avm/res/communication/email-service/domain/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3536602426579825020" + "version": "0.29.47.4906", + "templateHash": "8395896702104739839" }, "name": "Email Services Domains", "description": "This module deploys an Email Service Domain", @@ -43,6 +43,13 @@ "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": { @@ -178,6 +185,13 @@ } }, "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')]", @@ -224,20 +238,20 @@ "domain_roleAssignments": { "copy": { "name": "domain_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "domain" @@ -279,8 +293,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11776160114645029549" + "version": "0.29.47.4906", + "templateHash": "13801162652865106188" }, "name": "Sender Usernames", "description": "This module deploys an Sender", diff --git a/avm/res/communication/email-service/domain/sender-username/main.json b/avm/res/communication/email-service/domain/sender-username/main.json index 48abd0ade6..09d9805b15 100644 --- a/avm/res/communication/email-service/domain/sender-username/main.json +++ b/avm/res/communication/email-service/domain/sender-username/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "11776160114645029549" + "version": "0.29.47.4906", + "templateHash": "13801162652865106188" }, "name": "Sender Usernames", "description": "This module deploys an Sender", diff --git a/avm/res/communication/email-service/main.bicep b/avm/res/communication/email-service/main.bicep index 5be8bed90a..26225a901b 100644 --- a/avm/res/communication/email-service/main.bicep +++ b/avm/res/communication/email-service/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -103,14 +114,10 @@ resource email_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock } resource email_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(email.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(email.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -157,6 +164,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/communication/email-service/main.json b/avm/res/communication/email-service/main.json index 6732c700a2..590e6a347a 100644 --- a/avm/res/communication/email-service/main.json +++ b/avm/res/communication/email-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16758094060432541351" + "version": "0.29.47.4906", + "templateHash": "3207008154355010778" }, "name": "Email Services", "description": "This module deploys an Email Service", @@ -43,6 +43,13 @@ "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": { @@ -162,6 +169,13 @@ } }, "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')]", @@ -218,20 +232,20 @@ "email_roleAssignments": { "copy": { "name": "email_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Communication/emailServices/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Communication/emailServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Communication/emailServices', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "email" @@ -286,8 +300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4023506626147517594" + "version": "0.29.47.4906", + "templateHash": "8395896702104739839" }, "name": "Email Services Domains", "description": "This module deploys an Email Service Domain", @@ -324,6 +338,13 @@ "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": { @@ -459,6 +480,13 @@ } }, "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')]", @@ -505,20 +533,20 @@ "domain_roleAssignments": { "copy": { "name": "domain_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Communication/emailServices/{0}/domains/{1}', parameters('emailServiceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Communication/emailServices/domains', parameters('emailServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "domain" @@ -560,8 +588,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13309861087327282093" + "version": "0.29.47.4906", + "templateHash": "13801162652865106188" }, "name": "Sender Usernames", "description": "This module deploys an Sender", diff --git a/avm/res/communication/email-service/tests/e2e/max/main.test.bicep b/avm/res/communication/email-service/tests/e2e/max/main.test.bicep index b0b7e59d7f..fdbf792a41 100644 --- a/avm/res/communication/email-service/tests/e2e/max/main.test.bicep +++ b/avm/res/communication/email-service/tests/e2e/max/main.test.bicep @@ -59,11 +59,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'bdfa5270-8a55-466d-90d0-b5e96a90fadc' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -96,6 +98,7 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '1a441bec-9c57-49d1-9a83-b7fd62901413' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/communication/email-service/version.json b/avm/res/communication/email-service/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/communication/email-service/version.json +++ b/avm/res/communication/email-service/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/compute/availability-set/README.md b/avm/res/compute/availability-set/README.md index 5d9c10ce6c..d406d4fc8b 100644 --- a/avm/res/compute/availability-set/README.md +++ b/avm/res/compute/availability-set/README.md @@ -103,11 +103,13 @@ module availabilitySet 'br/public:avm/res/compute/availability-set:' = proximityPlacementGroupResourceId: '' roleAssignments: [ { + name: 'd9d13442-232d-4861-9ab9-bad5e90c4f71' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -159,11 +161,13 @@ module availabilitySet 'br/public:avm/res/compute/availability-set:' = "roleAssignments": { "value": [ { + "name": "d9d13442-232d-4861-9ab9-bad5e90c4f71", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -391,6 +395,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -441,6 +446,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/availability-set/main.bicep b/avm/res/compute/availability-set/main.bicep index e11ade47b6..032e989647 100644 --- a/avm/res/compute/availability-set/main.bicep +++ b/avm/res/compute/availability-set/main.bicep @@ -62,6 +62,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-availabilityset.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -111,14 +122,10 @@ resource availabilitySet_lock 'Microsoft.Authorization/locks@2020-05-01' = if (! } resource availabilitySet_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(availabilitySet.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(availabilitySet.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -155,6 +162,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/availability-set/main.json b/avm/res/compute/availability-set/main.json index 49f0362fe5..0c27739f3a 100644 --- a/avm/res/compute/availability-set/main.json +++ b/avm/res/compute/availability-set/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10353646856184732622" + "version": "0.29.47.4906", + "templateHash": "4688887403489577314" }, "name": "Availability Sets", "description": "This module deploys an Availability Set.", @@ -43,6 +43,13 @@ "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": { @@ -175,6 +182,13 @@ } }, "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')]", @@ -240,20 +254,20 @@ "availabilitySet_roleAssignments": { "copy": { "name": "availabilitySet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/availabilitySets/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/availabilitySets', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/availabilitySets', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "availabilitySet" diff --git a/avm/res/compute/availability-set/tests/e2e/max/main.test.bicep b/avm/res/compute/availability-set/tests/e2e/max/main.test.bicep index bc1a62c7df..beeb54bb36 100644 --- a/avm/res/compute/availability-set/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/availability-set/tests/e2e/max/main.test.bicep @@ -60,11 +60,13 @@ module testDeployment '../../../main.bicep' = [ proximityPlacementGroupResourceId: nestedDependencies.outputs.proximityPlacementGroupResourceId roleAssignments: [ { + name: 'd9d13442-232d-4861-9ab9-bad5e90c4f71' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/availability-set/version.json b/avm/res/compute/availability-set/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/compute/availability-set/version.json +++ b/avm/res/compute/availability-set/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/disk-encryption-set/README.md b/avm/res/compute/disk-encryption-set/README.md index 7ccaba8451..2acd6bcb96 100644 --- a/avm/res/compute/disk-encryption-set/README.md +++ b/avm/res/compute/disk-encryption-set/README.md @@ -237,11 +237,13 @@ module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -303,11 +305,13 @@ module diskEncryptionSet 'br/public:avm/res/compute/disk-encryption-set:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -599,6 +603,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -649,6 +654,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/disk-encryption-set/main.bicep b/avm/res/compute/disk-encryption-set/main.bicep index 9b83194abd..776806b032 100644 --- a/avm/res/compute/disk-encryption-set/main.bicep +++ b/avm/res/compute/disk-encryption-set/main.bicep @@ -96,6 +96,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-diskencryptionset.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -163,14 +174,14 @@ resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2023-10-02' = { } resource diskEncryptionSet_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(diskEncryptionSet.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + diskEncryptionSet.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -235,6 +246,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/disk-encryption-set/main.json b/avm/res/compute/disk-encryption-set/main.json index ce5413a973..94d6db420a 100644 --- a/avm/res/compute/disk-encryption-set/main.json +++ b/avm/res/compute/disk-encryption-set/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16145961046317503514" + "version": "0.29.47.4906", + "templateHash": "16517681525780428115" }, "name": "Disk Encryption Sets", "description": "This module deploys a Disk Encryption Set. The module will attempt to set permissions on the provided Key Vault for any used user-assigned identity.", @@ -65,6 +65,13 @@ "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": { @@ -222,6 +229,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -303,20 +317,20 @@ "diskEncryptionSet_roleAssignments": { "copy": { "name": "diskEncryptionSet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/diskEncryptionSets/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/diskEncryptionSets', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/diskEncryptionSets', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "diskEncryptionSet" @@ -374,8 +388,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "404460386674368842" + "version": "0.29.47.4906", + "templateHash": "7332173426743050645" } }, "parameters": { @@ -462,8 +476,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13413442691382205945" + "version": "0.29.47.4906", + "templateHash": "11254343639333423548" }, "name": "Key Vault Access Policies", "description": "This module deploys a Key Vault Access Policy.", diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep index bd27d88592..9adc104453 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/max/main.test.bicep @@ -65,11 +65,13 @@ module testDeployment '../../../main.bicep' = [ keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId roleAssignments: [ { + name: 'c331c327-6458-473a-9398-95b382c6f04f' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/disk-encryption-set/version.json b/avm/res/compute/disk-encryption-set/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/compute/disk-encryption-set/version.json +++ b/avm/res/compute/disk-encryption-set/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/disk/README.md b/avm/res/compute/disk/README.md index 4a99aec290..511e243714 100644 --- a/avm/res/compute/disk/README.md +++ b/avm/res/compute/disk/README.md @@ -256,11 +256,13 @@ module disk 'br/public:avm/res/compute/disk:' = { publicNetworkAccess: 'Enabled' roleAssignments: [ { + name: '89cc419c-8383-461d-9a70-5cfae4045a8d' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -333,11 +335,13 @@ module disk 'br/public:avm/res/compute/disk:' = { "roleAssignments": { "value": [ { + "name": "89cc419c-8383-461d-9a70-5cfae4045a8d", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -472,7 +476,7 @@ module disk 'br/public:avm/res/compute/disk:' = { | Parameter | Type | Description | | :-- | :-- | :-- | -| [`availabilityZone`](#parameter-availabilityzone) | int | If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. | +| [`availabilityZone`](#parameter-availabilityzone) | int | If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. Note that the availability zone number 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) and [Distribute VMs and disks across availability zones](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-high-availability#distribute-vms-and-disks-across-availability-zones). | | [`name`](#parameter-name) | string | The name of the disk that is being created. | | [`sku`](#parameter-sku) | string | The disks sku name. Can be . | @@ -515,7 +519,7 @@ module disk 'br/public:avm/res/compute/disk:' = { ### Parameter: `availabilityZone` -If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. +If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. Note that the availability zone number 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) and [Distribute VMs and disks across availability zones](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-high-availability#distribute-vms-and-disks-across-availability-zones). - Required: Yes - Type: int @@ -547,7 +551,6 @@ The disks sku name. Can be . [ 'Premium_LRS' 'Premium_ZRS' - 'Premium_ZRS' 'PremiumV2_LRS' 'Standard_LRS' 'StandardSSD_LRS' @@ -833,6 +836,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -883,6 +887,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/disk/main.bicep b/avm/res/compute/disk/main.bicep index 0f26a6bf38..38bde0070e 100644 --- a/avm/res/compute/disk/main.bicep +++ b/avm/res/compute/disk/main.bicep @@ -14,7 +14,6 @@ param location string = resourceGroup().location 'StandardSSD_LRS' 'UltraSSD_LRS' 'Premium_ZRS' - 'Premium_ZRS' 'PremiumV2_LRS' ]) @description('Required. The disks sku name. Can be .') @@ -125,7 +124,7 @@ param publicNetworkAccess string = 'Disabled' @description('Optional. True if the image from which the OS disk is created supports accelerated networking.') param acceleratedNetwork bool = false -@description('Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used.') +@description('Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. Note that the availability zone number 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) and [Distribute VMs and disks across availability zones](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-high-availability#distribute-vms-and-disks-across-availability-zones).') @allowed([ 0 1 @@ -180,6 +179,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-disk.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -260,14 +270,10 @@ resource disk_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock } resource disk_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(disk.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(disk.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -304,6 +310,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/disk/main.json b/avm/res/compute/disk/main.json index cfa9db25b1..a7a1c1ce18 100644 --- a/avm/res/compute/disk/main.json +++ b/avm/res/compute/disk/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13916693503285756379" + "version": "0.29.47.4906", + "templateHash": "14687427563063774670" }, "name": "Compute Disks", "description": "This module deploys a Compute Disk", @@ -43,6 +43,13 @@ "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": { @@ -127,7 +134,6 @@ "StandardSSD_LRS", "UltraSSD_LRS", "Premium_ZRS", - "Premium_ZRS", "PremiumV2_LRS" ], "metadata": { @@ -336,7 +342,7 @@ 3 ], "metadata": { - "description": "Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used." + "description": "Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. Note that the availability zone number 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) and [Distribute VMs and disks across availability zones](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-high-availability#distribute-vms-and-disks-across-availability-zones)." } }, "lock": { @@ -367,6 +373,13 @@ } }, "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')]", "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]", @@ -454,20 +467,20 @@ "disk_roleAssignments": { "copy": { "name": "disk_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/disks/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/disks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/disks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "disk" diff --git a/avm/res/compute/disk/tests/e2e/import/main.test.bicep b/avm/res/compute/disk/tests/e2e/import/main.test.bicep index ec552e0452..79fe97f09a 100644 --- a/avm/res/compute/disk/tests/e2e/import/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/import/main.test.bicep @@ -17,8 +17,8 @@ param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'cdimp' -@description('Generated. Do not provide a value! This date value is used to generate a unique image template name.') -param baseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') +@description('Generated. Do not provide a value! This date value is used to generate a unique image template name as the resource is not idempotent.') +param uniqueBaseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -41,7 +41,7 @@ module nestedDependencies 'dependencies.bicep' = { managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' location: resourceLocation storageAccountName: 'dep${namePrefix}sa${serviceShort}01' - imageTemplateName: 'dep-${namePrefix}-imgt-${serviceShort}-${baseTime}' + imageTemplateName: 'dep-${namePrefix}-imgt-${serviceShort}-${uniqueString(uniqueBaseTime)}' triggerImageDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}-triggerImageTemplate' copyVhdDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}-copyVhdToStorage' } diff --git a/avm/res/compute/disk/tests/e2e/max/main.test.bicep b/avm/res/compute/disk/tests/e2e/max/main.test.bicep index 33c8b5bd54..ac334cb1e8 100644 --- a/avm/res/compute/disk/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/max/main.test.bicep @@ -65,11 +65,13 @@ module testDeployment '../../../main.bicep' = [ publicNetworkAccess: 'Enabled' roleAssignments: [ { + name: '89cc419c-8383-461d-9a70-5cfae4045a8d' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/disk/version.json b/avm/res/compute/disk/version.json index 9481fea58e..c177b1bb58 100644 --- a/avm/res/compute/disk/version.json +++ b/avm/res/compute/disk/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/compute/gallery/README.md b/avm/res/compute/gallery/README.md index 6393269e82..b5f3018df8 100644 --- a/avm/res/compute/gallery/README.md +++ b/avm/res/compute/gallery/README.md @@ -106,9 +106,20 @@ module gallery 'br/public:avm/res/compute/gallery:' = { name: 'cgmax-appd-002' roleAssignments: [ { + name: '4ef8d3d3-54be-4522-92c3-284977292d87' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] supportedOSType: 'Windows' @@ -270,11 +281,13 @@ module gallery 'br/public:avm/res/compute/gallery:' = { } roleAssignments: [ { + name: '3bd58a78-108d-4f87-b404-0a03e49303d8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -321,9 +334,20 @@ module gallery 'br/public:avm/res/compute/gallery:' = { "name": "cgmax-appd-002", "roleAssignments": [ { + "name": "4ef8d3d3-54be-4522-92c3-284977292d87", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "supportedOSType": "Windows" @@ -493,11 +517,13 @@ module gallery 'br/public:avm/res/compute/gallery:' = { "roleAssignments": { "value": [ { + "name": "3bd58a78-108d-4f87-b404-0a03e49303d8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1040,6 +1066,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1090,6 +1117,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/gallery/application/README.md b/avm/res/compute/gallery/application/README.md index 2c2bd6d4a1..6e21e4d8da 100644 --- a/avm/res/compute/gallery/application/README.md +++ b/avm/res/compute/gallery/application/README.md @@ -147,6 +147,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -197,6 +198,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/gallery/application/main.bicep b/avm/res/compute/gallery/application/main.bicep index c17ac86830..7658a25ad3 100644 --- a/avm/res/compute/gallery/application/main.bicep +++ b/avm/res/compute/gallery/application/main.bicep @@ -61,6 +61,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource gallery 'Microsoft.Compute/galleries@2022-03-03' existing = { name: galleryName } @@ -82,14 +93,10 @@ resource application 'Microsoft.Compute/galleries/applications@2022-03-03' = { } resource application_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(application.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(application.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -118,6 +125,9 @@ output location string = application.location // =============== // type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/gallery/application/main.json b/avm/res/compute/gallery/application/main.json index 0e4a0f094e..ffd5ebf2da 100644 --- a/avm/res/compute/gallery/application/main.json +++ b/avm/res/compute/gallery/application/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1567876913625134678" + "version": "0.29.47.4906", + "templateHash": "15495203212384882152" }, "name": "Compute Galleries Applications", "description": "This module deploys an Azure Compute Gallery Application.", @@ -18,6 +18,13 @@ "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": { @@ -168,6 +175,13 @@ } }, "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": { "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -206,20 +220,20 @@ "application_roleAssignments": { "copy": { "name": "application_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/galleries/{0}/applications/{1}', parameters('galleryName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "application" diff --git a/avm/res/compute/gallery/image/README.md b/avm/res/compute/gallery/image/README.md index b02c5674df..e665e751f9 100644 --- a/avm/res/compute/gallery/image/README.md +++ b/avm/res/compute/gallery/image/README.md @@ -339,6 +339,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -389,6 +390,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/gallery/image/main.bicep b/avm/res/compute/gallery/image/main.bicep index b622a892ad..3f8fc791a2 100644 --- a/avm/res/compute/gallery/image/main.bicep +++ b/avm/res/compute/gallery/image/main.bicep @@ -97,6 +97,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource gallery 'Microsoft.Compute/galleries@2023-07-03' existing = { name: galleryName } @@ -154,14 +165,10 @@ resource image 'Microsoft.Compute/galleries/images@2023-07-03' = { } resource image_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(image.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(image.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -190,6 +197,9 @@ output location string = image.location // =============== // type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/gallery/image/main.json b/avm/res/compute/gallery/image/main.json index ee490554ab..5d3ed725ba 100644 --- a/avm/res/compute/gallery/image/main.json +++ b/avm/res/compute/gallery/image/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "751325967553230167" + "version": "0.29.47.4906", + "templateHash": "16910019400576294016" }, "name": "Compute Galleries Image Definitions", "description": "This module deploys an Azure Compute Gallery Image Definition.", @@ -18,6 +18,13 @@ "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": { @@ -355,6 +362,13 @@ } }, "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": { "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -409,20 +423,20 @@ "image_roleAssignments": { "copy": { "name": "image_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/galleries/{0}/images/{1}', parameters('galleryName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "image" diff --git a/avm/res/compute/gallery/main.bicep b/avm/res/compute/gallery/main.bicep index 13acf14e95..ddf7b2d835 100644 --- a/avm/res/compute/gallery/main.bicep +++ b/avm/res/compute/gallery/main.bicep @@ -66,6 +66,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -113,14 +124,10 @@ resource gallery_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource gallery_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(gallery.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(gallery.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -216,6 +223,9 @@ type lockType = { } type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/gallery/main.json b/avm/res/compute/gallery/main.json index e806011f6a..1916ebf0db 100644 --- a/avm/res/compute/gallery/main.json +++ b/avm/res/compute/gallery/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10621558710592844448" + "version": "0.29.47.4906", + "templateHash": "9786326741816294970" }, "name": "Azure Compute Galleries", "description": "This module deploys an Azure Compute Gallery (formerly known as Shared Image Gallery).", @@ -42,6 +42,13 @@ "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": { @@ -423,6 +430,13 @@ } }, "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": { "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -482,20 +496,20 @@ "gallery_roleAssignments": { "copy": { "name": "gallery_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/galleries/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/galleries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/galleries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "gallery" @@ -559,8 +573,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1567876913625134678" + "version": "0.29.47.4906", + "templateHash": "15495203212384882152" }, "name": "Compute Galleries Applications", "description": "This module deploys an Azure Compute Gallery Application.", @@ -572,6 +586,13 @@ "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": { @@ -722,6 +743,13 @@ } }, "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": { "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -760,20 +788,20 @@ "application_roleAssignments": { "copy": { "name": "application_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/galleries/{0}/applications/{1}', parameters('galleryName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "application" @@ -906,8 +934,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "751325967553230167" + "version": "0.29.47.4906", + "templateHash": "16910019400576294016" }, "name": "Compute Galleries Image Definitions", "description": "This module deploys an Azure Compute Gallery Image Definition.", @@ -919,6 +947,13 @@ "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": { @@ -1256,6 +1291,13 @@ } }, "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": { "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -1310,20 +1352,20 @@ "image_roleAssignments": { "copy": { "name": "image_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/galleries/{0}/images/{1}', parameters('galleryName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "image" diff --git a/avm/res/compute/gallery/tests/e2e/max/main.test.bicep b/avm/res/compute/gallery/tests/e2e/max/main.test.bicep index 8d3bc53f81..f094c884e9 100644 --- a/avm/res/compute/gallery/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/gallery/tests/e2e/max/main.test.bicep @@ -66,7 +66,21 @@ module testDeployment '../../../main.bicep' = [ supportedOSType: 'Windows' roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: '4ef8d3d3-54be-4522-92c3-284977292d87' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -221,11 +235,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '3bd58a78-108d-4f87-b404-0a03e49303d8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/gallery/version.json b/avm/res/compute/gallery/version.json index 41fc8c654f..21226dd43f 100644 --- a/avm/res/compute/gallery/version.json +++ b/avm/res/compute/gallery/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/compute/image/README.md b/avm/res/compute/image/README.md index 1f3e26e39b..6d528c3dbc 100644 --- a/avm/res/compute/image/README.md +++ b/avm/res/compute/image/README.md @@ -121,11 +121,13 @@ module image 'br/public:avm/res/compute/image:' = { osState: 'Generalized' roleAssignments: [ { + name: '2dfcdedd-220c-4b6b-b8bd-58e22e0c5434' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -193,11 +195,13 @@ module image 'br/public:avm/res/compute/image:' = { "roleAssignments": { "value": [ { + "name": "2dfcdedd-220c-4b6b-b8bd-58e22e0c5434", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -490,6 +494,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -540,6 +545,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/image/main.bicep b/avm/res/compute/image/main.bicep index 14ccabeb7c..f0a50bcb5e 100644 --- a/avm/res/compute/image/main.bicep +++ b/avm/res/compute/image/main.bicep @@ -77,6 +77,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-image.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -139,14 +150,10 @@ resource image 'Microsoft.Compute/images@2022-11-01' = { } resource image_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(image.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(image.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -175,6 +182,9 @@ output location string = image.location // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/image/main.json b/avm/res/compute/image/main.json index dd125ea378..ac130635b0 100644 --- a/avm/res/compute/image/main.json +++ b/avm/res/compute/image/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7076106645511766005" + "version": "0.29.47.4906", + "templateHash": "14267908439723997981" }, "name": "Images", "description": "This module deploys a Compute Image.", @@ -18,6 +18,13 @@ "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": { @@ -214,6 +221,13 @@ } }, "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')]", @@ -273,20 +287,20 @@ "image_roleAssignments": { "copy": { "name": "image_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/images/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/images', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/images', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "image" diff --git a/avm/res/compute/image/tests/e2e/max/main.test.bicep b/avm/res/compute/image/tests/e2e/max/main.test.bicep index 2c88a950cd..53701c42e8 100644 --- a/avm/res/compute/image/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/image/tests/e2e/max/main.test.bicep @@ -68,11 +68,13 @@ module testDeployment '../../../main.bicep' = [ hyperVGeneration: 'V1' roleAssignments: [ { + name: '2dfcdedd-220c-4b6b-b8bd-58e22e0c5434' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/image/version.json b/avm/res/compute/image/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/compute/image/version.json +++ b/avm/res/compute/image/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/compute/proximity-placement-group/README.md b/avm/res/compute/proximity-placement-group/README.md index fc052ff287..9ecfc1afe8 100644 --- a/avm/res/compute/proximity-placement-group/README.md +++ b/avm/res/compute/proximity-placement-group/README.md @@ -114,11 +114,13 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr } roleAssignments: [ { + name: '9e0b6b99-ff4b-4c99-a2ce-3a2a1a880874' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -187,11 +189,13 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr "roleAssignments": { "value": [ { + "name": "9e0b6b99-ff4b-4c99-a2ce-3a2a1a880874", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -443,6 +447,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -493,6 +498,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/proximity-placement-group/main.bicep b/avm/res/compute/proximity-placement-group/main.bicep index ccb3285051..3077d4feb3 100644 --- a/avm/res/compute/proximity-placement-group/main.bicep +++ b/avm/res/compute/proximity-placement-group/main.bicep @@ -50,6 +50,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-proximityplacementgroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -93,14 +104,14 @@ resource proximityPlacementGroup_lock 'Microsoft.Authorization/locks@2020-05-01' } resource proximityPlacementGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(proximityPlacementGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + proximityPlacementGroup.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -137,6 +148,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/proximity-placement-group/main.json b/avm/res/compute/proximity-placement-group/main.json index 3f445f160d..4a8f0d0ae0 100644 --- a/avm/res/compute/proximity-placement-group/main.json +++ b/avm/res/compute/proximity-placement-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13404424550740780901" + "version": "0.29.47.4906", + "templateHash": "10464518989411933340" }, "name": "Proximity Placement Groups", "description": "This module deploys a Proximity Placement Group.", @@ -43,6 +43,13 @@ "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": { @@ -179,6 +186,13 @@ } }, "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')]", @@ -238,20 +252,20 @@ "proximityPlacementGroup_roleAssignments": { "copy": { "name": "proximityPlacementGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/proximityPlacementGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/proximityPlacementGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/compute/proximity-placement-group/tests/e2e/max/main.test.bicep b/avm/res/compute/proximity-placement-group/tests/e2e/max/main.test.bicep index f8225519b8..ae14f18cc5 100644 --- a/avm/res/compute/proximity-placement-group/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/proximity-placement-group/tests/e2e/max/main.test.bicep @@ -58,11 +58,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '9e0b6b99-ff4b-4c99-a2ce-3a2a1a880874' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/proximity-placement-group/version.json b/avm/res/compute/proximity-placement-group/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/compute/proximity-placement-group/version.json +++ b/avm/res/compute/proximity-placement-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/ssh-public-key/README.md b/avm/res/compute/ssh-public-key/README.md index 27ded42618..8998a853ad 100644 --- a/avm/res/compute/ssh-public-key/README.md +++ b/avm/res/compute/ssh-public-key/README.md @@ -106,11 +106,13 @@ module sshPublicKey 'br/public:avm/res/compute/ssh-public-key:' = { publicKey: '' roleAssignments: [ { + name: '74ec0421-c3f4-46f2-acf0-b519fe6fcf1c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -165,11 +167,13 @@ module sshPublicKey 'br/public:avm/res/compute/ssh-public-key:' = { "roleAssignments": { "value": [ { + "name": "74ec0421-c3f4-46f2-acf0-b519fe6fcf1c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -417,6 +421,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -467,6 +472,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/ssh-public-key/main.bicep b/avm/res/compute/ssh-public-key/main.bicep index 2dc480fe15..7f36ce438e 100644 --- a/avm/res/compute/ssh-public-key/main.bicep +++ b/avm/res/compute/ssh-public-key/main.bicep @@ -40,6 +40,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-sshpublickey.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -79,15 +90,11 @@ resource sshPublicKeyLock 'Microsoft.Authorization/locks@2020-05-01' = if (!empt scope: sshPublicKey } -resource sshPublicKeyRoleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(sshPublicKey.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) +resource sshPublicKey_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(sshPublicKey.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -116,6 +123,9 @@ output location string = sshPublicKey.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/ssh-public-key/main.json b/avm/res/compute/ssh-public-key/main.json index 6709e29a71..291581cfd6 100644 --- a/avm/res/compute/ssh-public-key/main.json +++ b/avm/res/compute/ssh-public-key/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9226852384191763443" + "version": "0.29.47.4906", + "templateHash": "16461530111525682871" }, "name": "Public SSH Keys", "description": "This module deploys a Public SSH Key.\n\n> Note: The resource does not auto-generate the key for you.", @@ -18,6 +18,13 @@ "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": { @@ -154,6 +161,13 @@ } }, "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')]", @@ -207,23 +221,23 @@ "sshPublicKey" ] }, - "sshPublicKeyRoleAssignments": { + "sshPublicKey_roleAssignments": { "copy": { - "name": "sshPublicKeyRoleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "sshPublicKey_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/sshPublicKeys/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/sshPublicKeys', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/sshPublicKeys', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "sshPublicKey" diff --git a/avm/res/compute/ssh-public-key/tests/e2e/max/main.test.bicep b/avm/res/compute/ssh-public-key/tests/e2e/max/main.test.bicep index edacd11616..6b73bf45da 100644 --- a/avm/res/compute/ssh-public-key/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/ssh-public-key/tests/e2e/max/main.test.bicep @@ -58,11 +58,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: '74ec0421-c3f4-46f2-acf0-b519fe6fcf1c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/ssh-public-key/version.json b/avm/res/compute/ssh-public-key/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/compute/ssh-public-key/version.json +++ b/avm/res/compute/ssh-public-key/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/virtual-machine-scale-set/README.md b/avm/res/compute/virtual-machine-scale-set/README.md index a6bb0e788f..c778e8d960 100644 --- a/avm/res/compute/virtual-machine-scale-set/README.md +++ b/avm/res/compute/virtual-machine-scale-set/README.md @@ -58,18 +58,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslinmin001' - osDisk: { - createOption: 'fromImage' - diskSizeGB: '128' - managedDisk: { - storageAccountType: 'Premium_LRS' - } - } - osType: 'Linux' - skuName: 'Standard_B12ms' - // Non-required parameters - disablePasswordAuthentication: true - location: '' nicConfigurations: [ { ipConfigurations: [ @@ -88,6 +76,18 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s nicSuffix: '-nic01' } ] + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Linux' + skuName: 'Standard_B12ms' + // Non-required parameters + disablePasswordAuthentication: true + location: '' publicKeys: [ { keyData: '' @@ -125,6 +125,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslinmin001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslinmin" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -147,26 +167,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "location": { "value": "" }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslinmin" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -204,6 +204,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslinmax001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsslinmax' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -297,24 +315,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsslinmax' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] publicKeys: [ { keyData: '' @@ -323,9 +323,21 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] roleAssignments: [ { + name: '8abf72f9-e918-4adc-b20b-c783b8799065' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] scaleSetFaultDomain: 1 @@ -369,6 +381,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslinmax001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslinmax" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -496,26 +528,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslinmax" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -526,10 +538,22 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s }, "roleAssignments": { "value": [ + { + "name": "8abf72f9-e918-4adc-b20b-c783b8799065", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, { "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "" } ] }, @@ -584,6 +608,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsslcmk001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsslcmk' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -615,24 +657,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s enabled: true } location: '' - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsslcmk' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] publicKeys: [ { keyData: '' @@ -670,6 +694,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsslcmk001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsslcmk" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -715,26 +759,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "location": { "value": "" }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsslcmk" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "publicKeys": { "value": [ { @@ -772,18 +796,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinmin001' - osDisk: { - createOption: 'fromImage' - diskSizeGB: '128' - managedDisk: { - storageAccountType: 'Premium_LRS' - } - } - osType: 'Windows' - skuName: 'Standard_B12ms' - // Non-required parameters - adminPassword: '' - location: '' nicConfigurations: [ { ipConfigurations: [ @@ -802,6 +814,18 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s nicSuffix: '-nic01' } ] + osDisk: { + createOption: 'fromImage' + diskSizeGB: '128' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + skuName: 'Standard_B12ms' + // Non-required parameters + adminPassword: '' + location: '' } } ``` @@ -833,28 +857,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinmin001" }, - "osDisk": { - "value": { - "createOption": "fromImage", - "diskSizeGB": "128", - "managedDisk": { - "storageAccountType": "Premium_LRS" - } - } - }, - "osType": { - "value": "Windows" - }, - "skuName": { - "value": "Standard_B12ms" - }, - // Non-required parameters - "adminPassword": { - "value": "" - }, - "location": { - "value": "" - }, "nicConfigurations": { "value": [ { @@ -873,7 +875,29 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ], "nicSuffix": "-nic01" } - ] + ] + }, + "osDisk": { + "value": { + "createOption": "fromImage", + "diskSizeGB": "128", + "managedDisk": { + "storageAccountType": "Premium_LRS" + } + } + }, + "osType": { + "value": "Windows" + }, + "skuName": { + "value": "Standard_B12ms" + }, + // Non-required parameters + "adminPassword": { + "value": "" + }, + "location": { + "value": "" } } } @@ -904,6 +928,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinmax001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsswinmax' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -979,6 +1021,14 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s extensionDSCConfig: { enabled: true } + extensionHealthConfig: { + enabled: true + settings: { + port: 80 + protocol: 'http' + requestPath: '/' + } + } extensionMonitoringAgentConfig: { enabled: true } @@ -996,29 +1046,23 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ + roleAssignments: [ { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsswinmax' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' + name: '1910de8c-4dab-4189-96bb-2feb68350fb8' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' } - ] - roleAssignments: [ { principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: '' } ] skuCapacity: 1 @@ -1061,6 +1105,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinmax001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsswinmax" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -1158,6 +1222,16 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "enabled": true } }, + "extensionHealthConfig": { + "value": { + "enabled": true, + "settings": { + "port": 80, + "protocol": "http", + "requestPath": "/" + } + } + }, "extensionMonitoringAgentConfig": { "value": { "enabled": true @@ -1185,32 +1259,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsswinmax" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "roleAssignments": { "value": [ + { + "name": "1910de8c-4dab-4189-96bb-2feb68350fb8", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, { "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "" } ] }, @@ -1262,6 +1328,24 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s version: 'latest' } name: 'cvmsswinwaf001' + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + publicIPAddressConfiguration: { + name: 'pip-cvmsswinwaf' + } + subnet: { + id: '' + } + } + } + ] + nicSuffix: '-nic01' + } + ] osDisk: { createOption: 'fromImage' diskSizeGB: '128' @@ -1350,24 +1434,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s '' ] } - nicConfigurations: [ - { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - publicIPAddressConfiguration: { - name: 'pip-cvmsswinwaf' - } - subnet: { - id: '' - } - } - } - ] - nicSuffix: '-nic01' - } - ] skuCapacity: 1 tags: { Environment: 'Non-Prod' @@ -1408,6 +1474,26 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s "name": { "value": "cvmsswinwaf001" }, + "nicConfigurations": { + "value": [ + { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "publicIPAddressConfiguration": { + "name": "pip-cvmsswinwaf" + }, + "subnet": { + "id": "" + } + } + } + ], + "nicSuffix": "-nic01" + } + ] + }, "osDisk": { "value": { "createOption": "fromImage", @@ -1526,26 +1612,6 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s ] } }, - "nicConfigurations": { - "value": [ - { - "ipConfigurations": [ - { - "name": "ipconfig1", - "properties": { - "publicIPAddressConfiguration": { - "name": "pip-cvmsswinwaf" - }, - "subnet": { - "id": "" - } - } - } - ], - "nicSuffix": "-nic01" - } - ] - }, "skuCapacity": { "value": 1 }, @@ -1597,6 +1663,7 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`availabilityZones`](#parameter-availabilityzones) | array | The virtual machine scale set zones. NOTE: Availability zones can only be set when you create the scale set. | | [`bootDiagnosticStorageAccountName`](#parameter-bootdiagnosticstorageaccountname) | string | Storage account used to store boot diagnostic information. Boot diagnostics will be disabled if no value is provided. | | [`bootDiagnosticStorageAccountUri`](#parameter-bootdiagnosticstorageaccounturi) | string | Storage account boot diagnostic base URI. | +| [`bypassPlatformSafetyChecksOnUserSchedule`](#parameter-bypassplatformsafetychecksonuserschedule) | bool | Enables customer to schedule patching without accidental upgrades. | | [`customData`](#parameter-customdata) | string | Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. | | [`dataDisks`](#parameter-datadisks) | array | 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 VM Scale sets. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | @@ -1613,9 +1680,10 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`extensionAzureDiskEncryptionConfig`](#parameter-extensionazurediskencryptionconfig) | object | 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. | | [`extensionCustomScriptConfig`](#parameter-extensioncustomscriptconfig) | object | The configuration for the [Custom Script] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionDependencyAgentConfig`](#parameter-extensiondependencyagentconfig) | object | The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed. | -| [`extensionDomainJoinConfig`](#parameter-extensiondomainjoinconfig) | object | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | +| [`extensionDomainJoinConfig`](#parameter-extensiondomainjoinconfig) | secureObject | The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionDomainJoinPassword`](#parameter-extensiondomainjoinpassword) | securestring | Required if name is specified. Password of the user specified in user parameter. | | [`extensionDSCConfig`](#parameter-extensiondscconfig) | object | The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed. | +| [`extensionHealthConfig`](#parameter-extensionhealthconfig) | object | Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionMonitoringAgentConfig`](#parameter-extensionmonitoringagentconfig) | object | The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionNetworkWatcherAgentConfig`](#parameter-extensionnetworkwatcheragentconfig) | object | The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed. | | [`gracePeriod`](#parameter-graceperiod) | string | The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M). | @@ -1631,12 +1699,15 @@ module virtualMachineScaleSet 'br/public:avm/res/compute/virtual-machine-scale-s | [`monitoringWorkspaceResourceId`](#parameter-monitoringworkspaceresourceid) | string | Resource ID of the monitoring log analytics workspace. | | [`orchestrationMode`](#parameter-orchestrationmode) | string | Specifies the orchestration mode for the virtual machine scale set. | | [`overprovision`](#parameter-overprovision) | bool | Specifies whether the Virtual Machine Scale Set should be overprovisioned. | +| [`patchAssessmentMode`](#parameter-patchassessmentmode) | string | VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. | +| [`patchMode`](#parameter-patchmode) | string | 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'. | | [`pauseTimeBetweenBatches`](#parameter-pausetimebetweenbatches) | string | The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. | | [`plan`](#parameter-plan) | object | 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. | | [`prioritizeUnhealthyInstances`](#parameter-prioritizeunhealthyinstances) | bool | Upgrade all unhealthy instances in a scale set before any healthy instances. | | [`provisionVMAgent`](#parameter-provisionvmagent) | bool | 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. | | [`proximityPlacementGroupResourceId`](#parameter-proximityplacementgroupresourceid) | string | Resource ID of a proximity placement group. | | [`publicKeys`](#parameter-publickeys) | array | The list of SSH public keys used to authenticate with linux based VMs. | +| [`rebootSetting`](#parameter-rebootsetting) | string | Specifies the reboot setting for all AutomaticByPlatform patch installation operations. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`rollbackFailedInstancesOnPolicyBreach`](#parameter-rollbackfailedinstancesonpolicybreach) | bool | Rollback failed instances to previous model if the Rolling Upgrade policy is violated. | | [`sasTokenValidityLength`](#parameter-sastokenvaliditylength) | string | 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. | @@ -1689,9 +1760,8 @@ Name of the VMSS. Configures NICs and PIPs. -- Required: No +- Required: Yes - Type: array -- Default: `[]` ### Parameter: `osDisk` @@ -1743,7 +1813,7 @@ Specifies whether automatic repairs should be enabled on the virtual machine sca - Required: No - Type: bool -- Default: `False` +- Default: `True` ### Parameter: `availabilityZones` @@ -1776,6 +1846,14 @@ Storage account boot diagnostic base URI. - Type: string - Default: `[format('.blob.{0}/', environment().suffixes.storage)]` +### Parameter: `bypassPlatformSafetyChecksOnUserSchedule` + +Enables customer to schedule patching without accidental upgrades. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `customData` Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. @@ -2031,7 +2109,7 @@ The configuration for the [Dependency Agent] extension. Must at least contain th The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed. - Required: No -- Type: object +- Type: secureObject - Default: ```Bicep { @@ -2060,6 +2138,24 @@ The configuration for the [Desired State Configuration] extension. Must at least } ``` +### Parameter: `extensionHealthConfig` + +Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed. + +- Required: No +- Type: object +- Default: + ```Bicep + { + enabled: true + settings: { + port: 80 + protocol: 'http' + requestPath: '/' + } + } + ``` + ### Parameter: `extensionMonitoringAgentConfig` The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed. @@ -2252,6 +2348,39 @@ Specifies whether the Virtual Machine Scale Set should be overprovisioned. - Type: bool - Default: `False` +### Parameter: `patchAssessmentMode` + +VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours. + +- Required: No +- Type: string +- Default: `'ImageDefault'` +- Allowed: + ```Bicep + [ + 'AutomaticByPlatform' + 'ImageDefault' + ] + ``` + +### Parameter: `patchMode` + +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'. + +- Required: No +- Type: string +- Default: `'AutomaticByPlatform'` +- Allowed: + ```Bicep + [ + '' + 'AutomaticByOS' + 'AutomaticByPlatform' + 'ImageDefault' + 'Manual' + ] + ``` + ### Parameter: `pauseTimeBetweenBatches` The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. @@ -2300,6 +2429,23 @@ The list of SSH public keys used to authenticate with linux based VMs. - Type: array - Default: `[]` +### Parameter: `rebootSetting` + +Specifies the reboot setting for all AutomaticByPlatform patch installation operations. + +- Required: No +- Type: string +- Default: `'IfRequired'` +- Allowed: + ```Bicep + [ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' + ] + ``` + ### Parameter: `roleAssignments` Array of role assignments to create. @@ -2322,6 +2468,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2372,6 +2519,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/compute/virtual-machine-scale-set/extension/main.json b/avm/res/compute/virtual-machine-scale-set/extension/main.json index 33c41add0b..db232f5964 100644 --- a/avm/res/compute/virtual-machine-scale-set/extension/main.json +++ b/avm/res/compute/virtual-machine-scale-set/extension/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17283728898456477586" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", diff --git a/avm/res/compute/virtual-machine-scale-set/main.bicep b/avm/res/compute/virtual-machine-scale-set/main.bicep index 40f0918867..82225709b6 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.bicep +++ b/avm/res/compute/virtual-machine-scale-set/main.bicep @@ -56,7 +56,7 @@ param scaleSetFaultDomain int = 1 param proximityPlacementGroupResourceId string = '' @description('Required. Configures NICs and PIPs.') -param nicConfigurations array = [] +param nicConfigurations array @description('Optional. Specifies the priority for the virtual machine.') @allowed([ @@ -85,6 +85,8 @@ param licenseType string = '' param extensionDomainJoinPassword string = '' @description('Optional. The configuration for the [Domain Join] extension. Must at least contain the ["enabled": true] property to be executed.') +@secure() +#disable-next-line secure-parameter-default param extensionDomainJoinConfig object = { enabled: false } @@ -117,6 +119,16 @@ param extensionAzureDiskEncryptionConfig object = { enabled: false } +@description('Optional. Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the ["enabled": true] property to be executed.') +param extensionHealthConfig object = { + enabled: true + settings: { + protocol: 'http' + port: 80 + requestPath: '/' + } +} + @description('Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the ["enabled": true] property to be executed.') param extensionDSCConfig object = { enabled: false @@ -179,7 +191,7 @@ param enableAutomaticOSUpgrade bool = false param disableAutomaticRollback bool = false @description('Optional. Specifies whether automatic repairs should be enabled on the virtual machine scale set.') -param automaticRepairsPolicyEnabled bool = false +param automaticRepairsPolicyEnabled bool = true @description('Optional. The amount of time for which automatic repairs are suspended due to a state change on VM. The grace time starts after the state change has completed. This helps avoid premature or accidental repairs. The time duration should be specified in ISO 8601 format. The minimum allowed grace period is 30 minutes (PT30M). The maximum allowed grace period is 90 minutes (PT90M).') param gracePeriod string = 'PT30M' @@ -202,6 +214,35 @@ param provisionVMAgent bool = true @description('Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning.') param enableAutomaticUpdates bool = true +@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\'.') +@allowed([ + 'AutomaticByPlatform' + 'AutomaticByOS' + 'Manual' + 'ImageDefault' + '' +]) +param patchMode string = 'AutomaticByPlatform' + +@description('Optional. Enables customer to schedule patching without accidental upgrades.') +param bypassPlatformSafetyChecksOnUserSchedule bool = true + +@description('Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations.') +@allowed([ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' +]) +param rebootSetting string = 'IfRequired' + +@description('Optional. VM guest patching assessment mode. Set it to \'AutomaticByPlatform\' to enable automatically check for updates every 24 hours.') +@allowed([ + 'AutomaticByPlatform' + 'ImageDefault' +]) +param patchAssessmentMode string = 'ImageDefault' + @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`.') param timeZone string = '' @@ -288,11 +329,35 @@ var linuxConfiguration = { publicKeys: publicKeysFormatted } provisionVMAgent: provisionVMAgent + patchSettings: (provisionVMAgent && (patchMode =~ 'AutomaticByPlatform' || patchMode =~ 'ImageDefault')) + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') + ? { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } + : null + } + : null } var windowsConfiguration = { provisionVMAgent: provisionVMAgent enableAutomaticUpdates: enableAutomaticUpdates + patchSettings: (provisionVMAgent && (patchMode =~ 'AutomaticByPlatform' || patchMode =~ 'AutomaticByOS' || patchMode =~ 'Manual')) + ? { + patchMode: patchMode + assessmentMode: patchAssessmentMode + automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') + ? { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } + : null + } + : null timeZone: empty(timeZone) ? null : timeZone additionalUnattendContent: empty(additionalUnattendContent) ? null : additionalUnattendContent winRM: !empty(winRM) @@ -391,6 +456,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-virtualmachinescaleset.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -534,6 +610,26 @@ resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2023-09-01' = { : null } } + extensionProfile: extensionHealthConfig.enabled + ? { + extensions: [ + { + name: 'HealthExtension' + properties: { + publisher: 'Microsoft.ManagedServices' + type: (osType == 'Windows' ? 'ApplicationHealthWindows' : 'ApplicationHealthLinux') + typeHandlerVersion: extensionHealthConfig.?typeHandlerVersion ?? '1.0' + autoUpgradeMinorVersion: extensionHealthConfig.?autoUpgradeMinorVersion ?? false + settings: { + protocol: extensionHealthConfig.?protocol ?? 'http' + port: extensionHealthConfig.?port ?? 80 + requestPath: extensionHealthConfig.?requestPath ?? '/' + } + } + } + ] + } + : null licenseType: empty(licenseType) ? null : licenseType priority: vmPriority evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null @@ -806,14 +902,10 @@ resource vmss_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05- ] resource vmss_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(vmss.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(vmss.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -861,6 +953,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/virtual-machine-scale-set/main.json b/avm/res/compute/virtual-machine-scale-set/main.json index 02551b6e2d..c24f34ca0b 100644 --- a/avm/res/compute/virtual-machine-scale-set/main.json +++ b/avm/res/compute/virtual-machine-scale-set/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16376458652852426905" + "version": "0.29.47.4906", + "templateHash": "1087573637114262417" }, "name": "Virtual Machine Scale Sets", "description": "This module deploys a Virtual Machine Scale Set.", @@ -66,6 +66,13 @@ "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": { @@ -332,7 +339,6 @@ }, "nicConfigurations": { "type": "array", - "defaultValue": [], "metadata": { "description": "Required. Configures NICs and PIPs." } @@ -383,7 +389,7 @@ } }, "extensionDomainJoinConfig": { - "type": "object", + "type": "secureObject", "defaultValue": { "enabled": false }, @@ -443,6 +449,20 @@ "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." } }, + "extensionHealthConfig": { + "type": "object", + "defaultValue": { + "enabled": true, + "settings": { + "protocol": "http", + "port": 80, + "requestPath": "/" + } + }, + "metadata": { + "description": "Optional. Turned on by default. The configuration for the [Application Health Monitoring] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, "extensionDSCConfig": { "type": "object", "defaultValue": { @@ -572,7 +592,7 @@ }, "automaticRepairsPolicyEnabled": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Specifies whether automatic repairs should be enabled on the virtual machine scale set." } @@ -618,6 +638,51 @@ "description": "Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning." } }, + "patchMode": { + "type": "string", + "defaultValue": "AutomaticByPlatform", + "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." + } + }, "timeZone": { "type": "string", "defaultValue": "", @@ -784,6 +849,11 @@ "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" } + }, + { + "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)))))]" } ], "linuxConfiguration": { @@ -791,11 +861,13 @@ "ssh": { "publicKeys": "[variables('publicKeysFormatted')]" }, - "provisionVMAgent": "[parameters('provisionVMAgent')]" + "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'), '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(), parameters('additionalUnattendContent'))]", "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), null())]" @@ -949,6 +1021,7 @@ "storageUri": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), format('https://{0}{1}', parameters('bootDiagnosticStorageAccountName'), parameters('bootDiagnosticStorageAccountUri')), null())]" } }, + "extensionProfile": "[if(parameters('extensionHealthConfig').enabled, createObject('extensions', createArray(createObject('name', 'HealthExtension', 'properties', createObject('publisher', 'Microsoft.ManagedServices', 'type', if(equals(parameters('osType'), 'Windows'), 'ApplicationHealthWindows', 'ApplicationHealthLinux'), 'typeHandlerVersion', coalesce(tryGet(parameters('extensionHealthConfig'), 'typeHandlerVersion'), '1.0'), 'autoUpgradeMinorVersion', coalesce(tryGet(parameters('extensionHealthConfig'), 'autoUpgradeMinorVersion'), false()), 'settings', createObject('protocol', coalesce(tryGet(parameters('extensionHealthConfig'), 'protocol'), 'http'), 'port', coalesce(tryGet(parameters('extensionHealthConfig'), 'port'), 80), 'requestPath', coalesce(tryGet(parameters('extensionHealthConfig'), 'requestPath'), '/')))))), null())]", "licenseType": "[if(empty(parameters('licenseType')), null(), parameters('licenseType'))]", "priority": "[parameters('vmPriority')]", "evictionPolicy": "[if(parameters('enableEvictionPolicy'), 'Deallocate', null())]", @@ -1029,20 +1102,20 @@ "vmss_roleAssignments": { "copy": { "name": "vmss_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/virtualMachineScaleSets/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/virtualMachineScaleSets', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/virtualMachineScaleSets', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "vmss" @@ -1089,8 +1162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1251,8 +1324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1420,8 +1493,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1579,8 +1652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1737,8 +1810,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -1899,8 +1972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -2067,8 +2140,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", @@ -2229,8 +2302,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5793562007935023852" + "version": "0.29.47.4906", + "templateHash": "10358696382777462468" }, "name": "Virtual Machine Scale Set Extensions", "description": "This module deploys a Virtual Machine Scale Set Extension.", diff --git a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.max/main.test.bicep b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.max/main.test.bicep index 75ece5bcaf..391d985bcd 100644 --- a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.max/main.test.bicep +++ b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.max/main.test.bicep @@ -190,8 +190,23 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '8abf72f9-e918-4adc-b20b-c783b8799065' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId - roleDefinitionIdOrName: 'Reader' principalType: 'ServicePrincipal' } ] diff --git a/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep index ae46d40a0c..344ca00812 100644 --- a/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine-scale-set/tests/e2e/windows.max/main.test.bicep @@ -161,6 +161,14 @@ module testDeployment '../../../main.bicep' = [ extensionNetworkWatcherAgentConfig: { enabled: true } + extensionHealthConfig: { + enabled: true + settings: { + protocol: 'http' + port: 80 + requestPath: '/' + } + } lock: { kind: 'CanNotDelete' name: 'myCustomLockName' @@ -185,8 +193,23 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '1910de8c-4dab-4189-96bb-2feb68350fb8' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId - roleDefinitionIdOrName: 'Reader' principalType: 'ServicePrincipal' } ] diff --git a/avm/res/compute/virtual-machine-scale-set/version.json b/avm/res/compute/virtual-machine-scale-set/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/compute/virtual-machine-scale-set/version.json +++ b/avm/res/compute/virtual-machine-scale-set/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index f6fc002f01..8e6ccaf128 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -19,10 +19,11 @@ This module deploys a Virtual Machine with one or multiple NICs and optionally o | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Automanage/configurationProfileAssignments` | [2022-05-04](https://learn.microsoft.com/en-us/azure/templates) | -| `Microsoft.Compute/virtualMachines` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-09-01/virtualMachines) | +| `Microsoft.Compute/virtualMachines` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2024-03-01/virtualMachines) | | `Microsoft.Compute/virtualMachines/extensions` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines/extensions) | | `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | | `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | +| `Microsoft.Insights/dataCollectionRuleAssociations` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-11/dataCollectionRuleAssociations) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | | `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | @@ -415,9 +416,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { publicIpNameSuffix: '-pip-01' roleAssignments: [ { + name: '696e6067-3ddc-4b71-bf97-9caebeba441a' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] zones: [ @@ -432,9 +444,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { name: 'nic-test-01' roleAssignments: [ { + name: 'ff72f58d-a3cf-42fd-9c27-c61906bdddfe' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] } @@ -543,8 +566,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true - monitoringWorkspaceResourceId: '' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -580,11 +608,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { rebootSetting: 'IfRequired' roleAssignments: [ { + name: 'eb01de52-d2be-4272-a7b9-13de6c399e27' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -680,9 +710,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "publicIpNameSuffix": "-pip-01", "roleAssignments": [ { + "name": "696e6067-3ddc-4b71-bf97-9caebeba441a", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "zones": [ @@ -697,9 +738,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "name": "nic-test-01", "roleAssignments": [ { + "name": "ff72f58d-a3cf-42fd-9c27-c61906bdddfe", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] } @@ -846,8 +898,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, - "monitoringWorkspaceResourceId": "", "tags": { "Environment": "Non-Prod", "hidden-title": "This is visible in the resource name", @@ -899,11 +956,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "roleAssignments": { "value": [ { + "name": "eb01de52-d2be-4272-a7b9-13de6c399e27", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1148,6 +1207,12 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true tags: { Environment: 'Non-Prod' @@ -1470,6 +1535,12 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, "tags": { "Environment": "Non-Prod", @@ -2100,9 +2171,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { publicIpNameSuffix: '-pip-01' roleAssignments: [ { + name: 'e962e7c1-261a-4afd-b5ad-17a640a0b7bc' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] zones: [ @@ -2117,9 +2199,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { name: 'nic-test-01' roleAssignments: [ { + name: '95fc1cc2-05ed-4f5a-a22c-a6ca852df7e7' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] } @@ -2262,8 +2355,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } } extensionMonitoringAgentConfig: { + dataCollectionRuleAssociations: [ + { + dataCollectionRuleResourceId: '' + name: 'SendMetricsToLAW' + } + ] enabled: true - monitoringWorkspaceResourceId: '' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -2294,11 +2392,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { rebootSetting: 'IfRequired' roleAssignments: [ { + name: 'c70e8c48-6945-4607-9695-1098ba5a86ed' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -2395,9 +2495,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "publicIpNameSuffix": "-pip-01", "roleAssignments": [ { + "name": "e962e7c1-261a-4afd-b5ad-17a640a0b7bc", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "zones": [ @@ -2412,9 +2523,20 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "name": "nic-test-01", "roleAssignments": [ { + "name": "95fc1cc2-05ed-4f5a-a22c-a6ca852df7e7", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] } @@ -2599,8 +2721,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionMonitoringAgentConfig": { "value": { + "dataCollectionRuleAssociations": [ + { + "dataCollectionRuleResourceId": "", + "name": "SendMetricsToLAW" + } + ], "enabled": true, - "monitoringWorkspaceResourceId": "", "tags": { "Environment": "Non-Prod", "hidden-title": "This is visible in the resource name", @@ -2647,11 +2774,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "roleAssignments": { "value": [ { + "name": "c70e8c48-6945-4607-9695-1098ba5a86ed", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -3124,6 +3253,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`disablePasswordAuthentication`](#parameter-disablepasswordauthentication) | bool | Specifies whether password authentication should be disabled. | | [`enableAutomaticUpdates`](#parameter-enableautomaticupdates) | bool | 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. | | [`enableEvictionPolicy`](#parameter-enableevictionpolicy) | bool | Specifies the eviction policy for the low priority virtual machine. Will result in 'Deallocate' eviction policy. | +| [`enableHotpatching`](#parameter-enablehotpatching) | bool | 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'. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`encryptionAtHost`](#parameter-encryptionathost) | bool | 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. | | [`extensionAadJoinConfig`](#parameter-extensionaadjoinconfig) | object | 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". | @@ -3160,7 +3290,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`sasTokenValidityLength`](#parameter-sastokenvaliditylength) | string | 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. | | [`secureBootEnabled`](#parameter-securebootenabled) | bool | 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. | -| [`securityType`](#parameter-securitytype) | string | Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings. | +| [`securityType`](#parameter-securitytype) | string | 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. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`timeZone`](#parameter-timezone) | string | 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`. | | [`ultraSSDEnabled`](#parameter-ultrassdenabled) | bool | 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. | @@ -3654,6 +3784,14 @@ Specifies the eviction policy for the low priority virtual machine. Will result - Type: bool - Default: `False` +### Parameter: `enableHotpatching` + +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'. + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. @@ -3816,6 +3954,7 @@ The configuration for the [Monitoring Agent] extension. Must at least contain th - Default: ```Bicep { + dataCollectionRuleAssociations: [] enabled: false } ``` @@ -4088,6 +4227,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -4138,6 +4278,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -4173,11 +4320,19 @@ Specifies whether secure boot should be enabled on the virtual machine. This par ### Parameter: `securityType` -Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings. +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. - Required: No - Type: string - Default: `''` +- Allowed: + ```Bicep + [ + '' + 'ConfidentialVM' + 'TrustedLaunch' + ] + ``` ### Parameter: `tags` diff --git a/avm/res/compute/virtual-machine/extension/main.json b/avm/res/compute/virtual-machine/extension/main.json index 230becd81b..702433c26d 100644 --- a/avm/res/compute/virtual-machine/extension/main.json +++ b/avm/res/compute/virtual-machine/extension/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index 774777f75f..0a4f4f476a 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -14,7 +14,12 @@ param vmSize string @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.') param encryptionAtHost bool = true -@description('Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings.') +@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.') +@allowed([ + '' + 'ConfidentialVM' + 'TrustedLaunch' +]) param securityType string = '' @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.') @@ -159,6 +164,7 @@ param extensionAntiMalwareConfig object = { @description('Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the ["enabled": true] property to be executed.') param extensionMonitoringAgentConfig object = { enabled: false + dataCollectionRuleAssociations: [] } @description('Optional. The configuration for the [Dependency Agent] extension. Must at least contain the ["enabled": true] property to be executed.') @@ -281,6 +287,9 @@ param rebootSetting string = 'IfRequired' ]) param patchAssessmentMode string = 'ImageDefault' +@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\'.') +param enableHotpatching bool = false + @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`.') param timeZone string = '' @@ -327,6 +336,7 @@ var windowsConfiguration = { ? { patchMode: patchMode assessmentMode: patchAssessmentMode + enableHotpatching: (patchMode =~ 'AutomaticByPlatform') ? enableHotpatching : false automaticByPlatformSettings: (patchMode =~ 'AutomaticByPlatform') ? { bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule @@ -434,6 +444,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.compute-virtualmachine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -482,7 +503,7 @@ module vm_nic 'modules/nic-configuration.bicep' = [ } ] -resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = { +resource vm 'Microsoft.Compute/virtualMachines@2024-03-01' = { name: name location: location identity: identity @@ -738,29 +759,6 @@ module vm_microsoftAntiMalwareExtension 'extension/main.bicep' = if (extensionAn ] } -resource vm_logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId)) { - name: last(split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : 'law'), - '/' - ))! - scope: az.resourceGroup( - split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : '//'), - '/' - )[2], - split( - (!empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? extensionMonitoringAgentConfig.monitoringWorkspaceId - : '////'), - '/' - )[4] - ) -} - module vm_azureMonitorAgentExtension 'extension/main.bicep' = if (extensionMonitoringAgentConfig.enabled) { name: '${uniqueString(deployment().name, location)}-VM-AzureMonitorAgent' params: { @@ -772,25 +770,27 @@ module vm_azureMonitorAgentExtension 'extension/main.bicep' = if (extensionMonit typeHandlerVersion: extensionMonitoringAgentConfig.?typeHandlerVersion ?? (osType == 'Windows' ? '1.22' : '1.29') autoUpgradeMinorVersion: extensionMonitoringAgentConfig.?autoUpgradeMinorVersion ?? true enableAutomaticUpgrade: extensionMonitoringAgentConfig.?enableAutomaticUpgrade ?? false - settings: { - workspaceId: !empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? vm_logAnalyticsWorkspace.properties.customerId - : '' - GCS_AUTO_CONFIG: osType == 'Linux' ? true : null - } supressFailures: extensionMonitoringAgentConfig.?supressFailures ?? false tags: extensionMonitoringAgentConfig.?tags ?? tags - protectedSettings: { - workspaceKey: !empty(extensionMonitoringAgentConfig.?monitoringWorkspaceId ?? '') - ? vm_logAnalyticsWorkspace.listKeys().primarySharedKey - : '' - } } dependsOn: [ vm_microsoftAntiMalwareExtension ] } +resource vm_dataCollectionRuleAssociations 'Microsoft.Insights/dataCollectionRuleAssociations@2023-03-11' = [ + for (dataCollectionRuleAssociation, index) in extensionMonitoringAgentConfig.dataCollectionRuleAssociations: if (extensionMonitoringAgentConfig.enabled) { + name: dataCollectionRuleAssociation.name + scope: vm + properties: { + dataCollectionRuleId: dataCollectionRuleAssociation.dataCollectionRuleResourceId + } + dependsOn: [ + vm_azureMonitorAgentExtension + ] + } +] + module vm_dependencyAgentExtension 'extension/main.bicep' = if (extensionDependencyAgentConfig.enabled) { name: '${uniqueString(deployment().name, location)}-VM-DependencyAgent' params: { @@ -1065,14 +1065,10 @@ resource vm_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? } resource vm_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(vm.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(vm.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -1120,6 +1116,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index 1743a90ecf..dc46cdf5ed 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7526507591610306393" + "version": "0.29.47.4906", + "templateHash": "17996854249282289438" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -66,6 +66,13 @@ "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": { @@ -337,8 +344,13 @@ "securityType": { "type": "string", "defaultValue": "", + "allowedValues": [ + "", + "ConfidentialVM", + "TrustedLaunch" + ], "metadata": { - "description": "Optional. Specifies the SecurityType of the virtual machine. It is set as TrustedLaunch to enable UefiSettings." + "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": { @@ -618,7 +630,8 @@ "extensionMonitoringAgentConfig": { "type": "object", "defaultValue": { - "enabled": false + "enabled": false, + "dataCollectionRuleAssociations": [] }, "metadata": { "description": "Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." @@ -841,6 +854,13 @@ "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": "", @@ -879,6 +899,11 @@ "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" } + }, + { + "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)))))]" } ], "linuxConfiguration": { @@ -892,7 +917,7 @@ "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'), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]", + "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(), parameters('additionalUnattendContent'))]", "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), null())]" @@ -950,7 +975,7 @@ }, "vm": { "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2023-09-01", + "apiVersion": "2024-03-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "identity": "[variables('identity')]", @@ -1098,14 +1123,23 @@ "vm" ] }, - "vm_logAnalyticsWorkspace": { - "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", - "existing": true, - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-06-01", - "subscriptionId": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2]]", - "resourceGroup": "[split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]]", - "name": "[last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))]" + "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')))]", @@ -1139,20 +1173,20 @@ "vm_roleAssignments": { "copy": { "name": "vm_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1209,8 +1243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6185170122493883924" + "version": "0.29.47.4906", + "templateHash": "1517797599230349789" } }, "definitions": { @@ -2761,8 +2795,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2970,8 +3004,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3175,8 +3209,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3367,22 +3401,11 @@ "enableAutomaticUpgrade": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'enableAutomaticUpgrade'), false())]" }, - "settings": { - "value": { - "workspaceId": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), reference('vm_logAnalyticsWorkspace').customerId, '')]", - "GCS_AUTO_CONFIG": "[if(equals(parameters('osType'), 'Linux'), true(), null())]" - } - }, "supressFailures": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" }, "tags": { "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" - }, - "protectedSettings": { - "value": { - "workspaceKey": "[if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '//'), '/')[2], split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, '////'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(if(not(empty(coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId'), ''))), parameters('extensionMonitoringAgentConfig').monitoringWorkspaceId, 'law'), '/'))), '2021-06-01').primarySharedKey, '')]" - } } }, "template": { @@ -3392,8 +3415,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3548,7 +3571,6 @@ }, "dependsOn": [ "vm", - "vm_logAnalyticsWorkspace", "vm_microsoftAntiMalwareExtension" ] }, @@ -3598,8 +3620,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3798,8 +3820,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4002,8 +4024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4214,8 +4236,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4418,8 +4440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4620,8 +4642,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4831,8 +4853,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -5034,8 +5056,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15287507275706455668" + "version": "0.29.47.4906", + "templateHash": "10657605324993327332" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -5233,8 +5255,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4311417160342913977" + "version": "0.29.47.4906", + "templateHash": "4676289166883418684" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", @@ -5368,14 +5390,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('vm', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('vm', '2023-09-01', 'full').location]" + "value": "[reference('vm', '2024-03-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep index 3f7b866feb..b0b2b53a5a 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep @@ -28,12 +28,18 @@ param sshDeploymentScriptName string @description('Required. The name of the SSH Key to create.') param sshKeyName string +@description('Required. The name of the data collection rule.') +param dcrName string + @description('Optional. The location to deploy to.') param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -315,6 +321,93 @@ resource sshKey 'Microsoft.Compute/sshPublicKeys@2022-03-01' = { } } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Linux' + properties: { + 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' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} + @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -359,3 +452,6 @@ output storageAccountCSEFileName string = storageAccountCSEFileName @description('The Public Key of the created SSH Key.') output SSHKeyPublicKey string = sshKey.properties.publicKey + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep index 1af8274f56..6b1d1a4e1e 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep @@ -20,6 +20,10 @@ param serviceShort string = 'cvmlinmax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.') +@secure() +param backupManagementServiceEnterpriseApplicationObjectId string = '' + // ============ // // Dependencies // // ============ // @@ -46,7 +50,9 @@ module nestedDependencies 'dependencies.bicep' = { storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' sshDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -107,7 +113,21 @@ module testDeployment '../../../main.bicep' = { ] roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: '696e6067-3ddc-4b71-bf97-9caebeba441a' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -133,7 +153,21 @@ module testDeployment '../../../main.bicep' = { name: 'nic-test-01' roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: 'ff72f58d-a3cf-42fd-9c27-c61906bdddfe' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -259,12 +293,17 @@ module testDeployment '../../../main.bicep' = { } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' Role: 'DeploymentValidation' } - monitoringWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } extensionNetworkWatcherAgentConfig: { enabled: true @@ -286,11 +325,13 @@ module testDeployment '../../../main.bicep' = { ] roleAssignments: [ { + name: 'eb01de52-d2be-4272-a7b9-13de6c399e27' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep index 6f2587a5df..795ab9dfa7 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep @@ -34,6 +34,12 @@ param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. The name of the data collection rule.') +param dcrName string + +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -324,6 +330,93 @@ resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@202 location: location } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Windows' + properties: { + 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' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} + @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -371,3 +464,6 @@ output storageAccountCSEFileUrl string = '${storageAccount.properties.primaryEnd @description('The resource ID of the created Proximity Placement Group.') output proximityPlacementGroupResourceId string = proximityPlacementGroup.id + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep index bf307d29c7..fd619b1589 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep @@ -24,6 +24,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.') +@secure() +param backupManagementServiceEnterpriseApplicationObjectId string = '' + // ============ // // Dependencies // // ============ // @@ -50,7 +54,9 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}01' storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -286,6 +292,12 @@ module testDeployment '../../../main.bicep' = [ } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep index 6f1ef21abd..a492ad2a5a 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep @@ -31,6 +31,12 @@ param location string = resourceGroup().location @description('Required. The object ID of the Backup Management Service Enterprise Application. Required for Customer-Managed-Keys.') param backupManagementServiceApplicationObjectId string +@description('Required. The name of the data collection rule.') +param dcrName string + +@description('Required. Resource ID of the log analytics worspace to stream logs from Azure monitoring agent.') +param logAnalyticsWorkspaceResourceId string + var storageAccountCSEFileName = 'scriptExtensionMasterInstaller.ps1' var addressPrefix = '10.0.0.0/16' @@ -288,6 +294,92 @@ resource proximityPlacementGroup 'Microsoft.Compute/proximityPlacementGroups@202 location: location } +resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { + name: dcrName + location: location + kind: 'Windows' + properties: { + 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' + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: logAnalyticsWorkspaceResourceId + name: 'la--1264800308' + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-Perf' + ] + destinations: [ + 'la--1264800308' + ] + transformKql: 'source' + outputStream: 'Microsoft-Perf' + } + ] + } +} @description('The resource ID of the created Virtual Network Subnet.') output subnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -332,3 +424,6 @@ output storageAccountCSEFileUrl string = '${storageAccount.properties.primaryEnd @description('The resource ID of the created Proximity Placement Group.') output proximityPlacementGroupResourceId string = proximityPlacementGroup.id + +@description('The resource ID of the created data collection rule.') +output dataCollectionRuleResourceId string = dcr.id diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep index 553f9b3daa..ac79b215ce 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep @@ -24,6 +24,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.') +@secure() +param backupManagementServiceEnterpriseApplicationObjectId string = '' + // ============ // // Dependencies // // ============ // @@ -49,7 +53,9 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}01' storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}' proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}' - backupManagementServiceApplicationObjectId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Tenant-specific Backup Management Service Enterprise Application Object Id + backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId + dcrName: 'dep-${namePrefix}-dcr-${serviceShort}' + logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } } @@ -112,7 +118,21 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: 'e962e7c1-261a-4afd-b5ad-17a640a0b7bc' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -139,7 +159,21 @@ module testDeployment '../../../main.bicep' = [ enableIPForwarding: true roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: '95fc1cc2-05ed-4f5a-a22c-a6ca852df7e7' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -299,12 +333,17 @@ module testDeployment '../../../main.bicep' = [ } extensionMonitoringAgentConfig: { enabled: true + dataCollectionRuleAssociations: [ + { + name: 'SendMetricsToLAW' + dataCollectionRuleResourceId: nestedDependencies.outputs.dataCollectionRuleResourceId + } + ] tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' Role: 'DeploymentValidation' } - monitoringWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } extensionNetworkWatcherAgentConfig: { enabled: true @@ -321,11 +360,13 @@ module testDeployment '../../../main.bicep' = [ proximityPlacementGroupResourceId: nestedDependencies.outputs.proximityPlacementGroupResourceId roleAssignments: [ { + name: 'c70e8c48-6945-4607-9695-1098ba5a86ed' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/compute/virtual-machine/version.json b/avm/res/compute/virtual-machine/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/compute/virtual-machine/version.json +++ b/avm/res/compute/virtual-machine/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/consumption/budget/README.md b/avm/res/consumption/budget/README.md index 4b4f942775..927392c5f1 100644 --- a/avm/res/consumption/budget/README.md +++ b/avm/res/consumption/budget/README.md @@ -26,8 +26,9 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/consumption/budget:`. - [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [WAF-aligned](#example-3-waf-aligned) +- [Using `thresholdType` `Forecasted`](#example-2-using-thresholdtype-forecasted) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -89,7 +90,71 @@ module budget 'br/public:avm/res/consumption/budget:' = {

-### Example 2: _Using large parameter set_ +### Example 2: _Using `thresholdType` `Forecasted`_ + +This instance deploys the module with the minimum set of required parameters and `thresholdType` `Forecasted`. + + +

+ +via Bicep module + +```bicep +module budget 'br/public:avm/res/consumption/budget:' = { + name: 'budgetDeployment' + params: { + // Required parameters + amount: 500 + name: 'cbfcst001' + // Non-required parameters + contactEmails: [ + 'dummy@contoso.com' + ] + location: '' + thresholdType: 'Forecasted' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "amount": { + "value": 500 + }, + "name": { + "value": "cbfcst001" + }, + // Non-required parameters + "contactEmails": { + "value": [ + "dummy@contoso.com" + ] + }, + "location": { + "value": "" + }, + "thresholdType": { + "value": "Forecasted" + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -175,7 +240,7 @@ module budget 'br/public:avm/res/consumption/budget:' = {

-### Example 3: _WAF-aligned_ +### Example 4: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. diff --git a/avm/res/consumption/budget/main.json b/avm/res/consumption/budget/main.json index d51817dbd2..b68ced8a2d 100644 --- a/avm/res/consumption/budget/main.json +++ b/avm/res/consumption/budget/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13231547958069431465" + "version": "0.29.47.4906", + "templateHash": "9899827298268482806" }, "name": "Consumption Budgets", "description": "This module deploys a Consumption Budget for Subscriptions.", diff --git a/avm/res/consumption/budget/tests/e2e/forecasted/main.test.bicep b/avm/res/consumption/budget/tests/e2e/forecasted/main.test.bicep new file mode 100644 index 0000000000..91112be1f4 --- /dev/null +++ b/avm/res/consumption/budget/tests/e2e/forecasted/main.test.bicep @@ -0,0 +1,37 @@ +targetScope = 'subscription' + +metadata name = 'Using `thresholdType` `Forecasted`' +metadata description = 'This instance deploys the module with the minimum set of required parameters and `thresholdType` `Forecasted`.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cbfcst' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + name: '${uniqueString(deployment().name)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + amount: 500 + contactEmails: [ + 'dummy@contoso.com' + ] + thresholdType: 'Forecasted' + } + } +] diff --git a/avm/res/container-instance/container-group/README.md b/avm/res/container-instance/container-group/README.md index 983ca8cf49..96d77cc81b 100644 --- a/avm/res/container-instance/container-group/README.md +++ b/avm/res/container-instance/container-group/README.md @@ -206,7 +206,7 @@ module containerGroup 'br/public:avm/res/container-instance/container-group:' @@ -302,7 +302,7 @@ module containerGroup 'br/public:avm/res/container-instance/container-group:**Note**: To reference the module, please use the following syntax `br/public:avm/res/container-registry/registry:`. -- [Using only defaults](#example-1-using-only-defaults) -- [Using encryption with Customer-Managed-Key](#example-2-using-encryption-with-customer-managed-key) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [WAF-aligned](#example-4-waf-aligned) +- [Using cache rules](#example-1-using-cache-rules) +- [Using only defaults](#example-2-using-only-defaults) +- [Using encryption with Customer-Managed-Key](#example-3-using-encryption-with-customer-managed-key) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [Using `scopeMaps` in parameter set](#example-5-using-scopemaps-in-parameter-set) +- [WAF-aligned](#example-6-waf-aligned) -### Example 1: _Using only defaults_ +### Example 1: _Using cache rules_ + +This instance deploys the module with a credential set and a cache rule. + + +

+ +via Bicep module + +```bicep +module registry 'br/public:avm/res/container-registry/registry:' = { + name: 'registryDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + acrAdminUserEnabled: false + acrSku: 'Standard' + cacheRules: [ + { + credentialSetResourceId: '' + name: 'customRule' + sourceRepository: 'docker.io/library/hello-world' + targetRepository: 'cached-docker-hub/hello-world' + } + ] + credentialSets: [ + { + authCredentials: [ + { + name: 'Credential1' + passwordSecretIdentifier: '' + usernameSecretIdentifier: '' + } + ] + loginServer: 'docker.io' + managedIdentities: { + systemAssigned: true + } + name: 'default' + } + ] + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "acrAdminUserEnabled": { + "value": false + }, + "acrSku": { + "value": "Standard" + }, + "cacheRules": { + "value": [ + { + "credentialSetResourceId": "", + "name": "customRule", + "sourceRepository": "docker.io/library/hello-world", + "targetRepository": "cached-docker-hub/hello-world" + } + ] + }, + "credentialSets": { + "value": [ + { + "authCredentials": [ + { + "name": "Credential1", + "passwordSecretIdentifier": "", + "usernameSecretIdentifier": "" + } + ], + "loginServer": "docker.io", + "managedIdentities": { + "systemAssigned": true + }, + "name": "default" + } + ] + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -90,7 +202,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = {

-### Example 2: _Using encryption with Customer-Managed-Key_ +### Example 3: _Using encryption with Customer-Managed-Key_ This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. @@ -170,7 +282,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = {

-### Example 3: _Using large parameter set_ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -189,16 +301,6 @@ module registry 'br/public:avm/res/container-registry/registry:' = { acrAdminUserEnabled: false acrSku: 'Premium' azureADAuthenticationAsArmPolicyStatus: 'enabled' - cacheRules: [ - { - name: 'customRule' - sourceRepository: 'docker.io/library/hello-world' - targetRepository: 'cached-docker-hub/hello-world' - } - { - sourceRepository: 'docker.io/library/hello-world' - } - ] diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -259,11 +361,13 @@ module registry 'br/public:avm/res/container-registry/registry:' = { ] roleAssignments: [ { + name: '60395919-cfd3-47bf-8349-775ddebb255e' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -318,18 +422,6 @@ module registry 'br/public:avm/res/container-registry/registry:' = { "azureADAuthenticationAsArmPolicyStatus": { "value": "enabled" }, - "cacheRules": { - "value": [ - { - "name": "customRule", - "sourceRepository": "docker.io/library/hello-world", - "targetRepository": "cached-docker-hub/hello-world" - }, - { - "sourceRepository": "docker.io/library/hello-world" - } - ] - }, "diagnosticSettings": { "value": [ { @@ -409,11 +501,13 @@ module registry 'br/public:avm/res/container-registry/registry:' = { "roleAssignments": { "value": [ { + "name": "60395919-cfd3-47bf-8349-775ddebb255e", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -456,7 +550,79 @@ module registry 'br/public:avm/res/container-registry/registry:' = {

-### Example 4: _WAF-aligned_ +### Example 5: _Using `scopeMaps` in parameter set_ + +This instance deploys the module with the scopeMaps feature. + + +

+ +via Bicep module + +```bicep +module registry 'br/public:avm/res/container-registry/registry:' = { + name: 'registryDeployment' + params: { + // Required parameters + name: 'crrs001' + // Non-required parameters + acrSku: 'Standard' + location: '' + scopeMaps: [ + { + actions: [ + 'repositories/*/content/read' + ] + description: 'This is a test for scopeMaps feature.' + name: 'testscopemap' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "crrs001" + }, + // Non-required parameters + "acrSku": { + "value": "Standard" + }, + "location": { + "value": "" + }, + "scopeMaps": { + "value": [ + { + "actions": [ + "repositories/*/content/read" + ], + "description": "This is a test for scopeMaps feature.", + "name": "testscopemap" + } + ] + } + } +} +``` + +
+

+ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -615,7 +781,8 @@ module registry 'br/public:avm/res/container-registry/registry:' = { | [`acrSku`](#parameter-acrsku) | string | Tier of your Azure container registry. | | [`anonymousPullEnabled`](#parameter-anonymouspullenabled) | bool | Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers. | | [`azureADAuthenticationAsArmPolicyStatus`](#parameter-azureadauthenticationasarmpolicystatus) | string | The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled. | -| [`cacheRules`](#parameter-cacherules) | array | Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview)). | +| [`cacheRules`](#parameter-cacherules) | array | Array of Cache Rules. | +| [`credentialSets`](#parameter-credentialsets) | array | Array of Credential Sets. | | [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | | [`dataEndpointEnabled`](#parameter-dataendpointenabled) | bool | Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | @@ -634,6 +801,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = { | [`retentionPolicyDays`](#parameter-retentionpolicydays) | int | The number of days to retain an untagged manifest after which it gets purged. | | [`retentionPolicyStatus`](#parameter-retentionpolicystatus) | string | The value that indicates whether the retention policy is enabled or not. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`scopeMaps`](#parameter-scopemaps) | array | Scope maps setting. | | [`softDeletePolicyDays`](#parameter-softdeletepolicydays) | int | The number of days after which a soft-deleted item is permanently deleted. | | [`softDeletePolicyStatus`](#parameter-softdeletepolicystatus) | string | Soft Delete policy status. Default is disabled. | | [`tags`](#parameter-tags) | object | Tags of the resource. | @@ -697,11 +865,19 @@ The value that indicates whether the policy for using ARM audience token for a c ### Parameter: `cacheRules` -Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview)). +Array of Cache Rules. - Required: No - Type: array +### Parameter: `credentialSets` + +Array of Credential Sets. + +- Required: No +- Type: array +- Default: `[]` + ### Parameter: `customerManagedKey` The customer managed key definition. @@ -1299,6 +1475,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1349,6 +1526,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1461,6 +1645,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1511,6 +1696,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1528,6 +1720,47 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `scopeMaps` + +Scope maps setting. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`actions`](#parameter-scopemapsactions) | array | The list of scoped permissions for registry artifacts. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-scopemapsdescription) | string | The user friendly description of the scope map. | +| [`name`](#parameter-scopemapsname) | string | The name of the scope map. | + +### Parameter: `scopeMaps.actions` + +The list of scoped permissions for registry artifacts. + +- Required: Yes +- Type: array + +### Parameter: `scopeMaps.description` + +The user friendly description of the scope map. + +- Required: No +- Type: string + +### Parameter: `scopeMaps.name` + +The name of the scope map. + +- Required: No +- Type: string + ### Parameter: `softDeletePolicyDays` The number of days after which a soft-deleted item is permanently deleted. @@ -1600,6 +1833,8 @@ Whether or not zone redundancy is enabled for this container registry. | Output | Type | Description | | :-- | :-- | :-- | +| `credentialSetsResourceIds` | array | The Resource IDs of the ACR Credential Sets. | +| `credentialSetsSystemAssignedMIPrincipalIds` | array | The Principal IDs of the ACR Credential Sets system-assigned identities. | | `location` | string | The location the resource was deployed into. | | `loginServer` | string | The reference to the Azure container registry. | | `name` | string | The Name of the Azure container registry. | diff --git a/avm/res/container-registry/registry/cache-rules/README.md b/avm/res/container-registry/registry/cache-rule/README.md similarity index 99% rename from avm/res/container-registry/registry/cache-rules/README.md rename to avm/res/container-registry/registry/cache-rule/README.md index 0c951e975b..0e11d412f6 100644 --- a/avm/res/container-registry/registry/cache-rules/README.md +++ b/avm/res/container-registry/registry/cache-rule/README.md @@ -22,6 +22,7 @@ Cache for Azure Container Registry (Preview) feature allows users to cache conta | Parameter | Type | Description | | :-- | :-- | :-- | +| [`credentialSetResourceId`](#parameter-credentialsetresourceid) | string | The resource ID of the credential store which is associated with the cache rule. | | [`registryName`](#parameter-registryname) | string | The name of the parent registry. Required if the template is used in a standalone deployment. | | [`sourceRepository`](#parameter-sourcerepository) | string | Source repository pulled from upstream. | @@ -29,29 +30,28 @@ Cache for Azure Container Registry (Preview) feature allows users to cache conta | Parameter | Type | Description | | :-- | :-- | :-- | -| [`credentialSetResourceId`](#parameter-credentialsetresourceid) | string | The resource ID of the credential store which is associated with the cache rule. | | [`name`](#parameter-name) | string | The name of the cache rule. Will be dereived from the source repository name if not defined. | | [`targetRepository`](#parameter-targetrepository) | string | Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}. | -### Parameter: `registryName` +### Parameter: `credentialSetResourceId` -The name of the parent registry. Required if the template is used in a standalone deployment. +The resource ID of the credential store which is associated with the cache rule. - Required: Yes - Type: string -### Parameter: `sourceRepository` +### Parameter: `registryName` -Source repository pulled from upstream. +The name of the parent registry. Required if the template is used in a standalone deployment. - Required: Yes - Type: string -### Parameter: `credentialSetResourceId` +### Parameter: `sourceRepository` -The resource ID of the credential store which is associated with the cache rule. +Source repository pulled from upstream. -- Required: No +- Required: Yes - Type: string ### Parameter: `name` diff --git a/avm/res/container-registry/registry/cache-rules/main.bicep b/avm/res/container-registry/registry/cache-rule/main.bicep similarity index 94% rename from avm/res/container-registry/registry/cache-rules/main.bicep rename to avm/res/container-registry/registry/cache-rule/main.bicep index a2d0a16dbe..55a1c1387c 100644 --- a/avm/res/container-registry/registry/cache-rules/main.bicep +++ b/avm/res/container-registry/registry/cache-rule/main.bicep @@ -14,8 +14,8 @@ param sourceRepository string @description('Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}.') param targetRepository string = sourceRepository -@description('Optional. The resource ID of the credential store which is associated with the cache rule.') -param credentialSetResourceId string? +@description('Required. The resource ID of the credential store which is associated with the cache rule.') +param credentialSetResourceId string resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' existing = { name: registryName diff --git a/avm/res/container-registry/registry/cache-rules/main.json b/avm/res/container-registry/registry/cache-rule/main.json similarity index 85% rename from avm/res/container-registry/registry/cache-rules/main.json rename to avm/res/container-registry/registry/cache-rule/main.json index 6f53fba613..c517e75779 100644 --- a/avm/res/container-registry/registry/cache-rules/main.json +++ b/avm/res/container-registry/registry/cache-rule/main.json @@ -1,12 +1,11 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15228477210534278013" + "version": "0.29.47.4906", + "templateHash": "4294329625336671928" }, "name": "Container Registries Cache", "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", @@ -41,20 +40,13 @@ }, "credentialSetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + "description": "Required. The resource ID of the credential store which is associated with the cache rule." } } }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "cacheRule": { + "resources": [ + { "type": "Microsoft.ContainerRegistry/registries/cacheRules", "apiVersion": "2023-06-01-preview", "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", @@ -62,12 +54,9 @@ "sourceRepository": "[parameters('sourceRepository')]", "targetRepository": "[parameters('targetRepository')]", "credentialSetResourceId": "[parameters('credentialSetResourceId')]" - }, - "dependsOn": [ - "registry" - ] + } } - }, + ], "outputs": { "name": { "type": "string", diff --git a/avm/res/container-registry/registry/credential-set/README.md b/avm/res/container-registry/registry/credential-set/README.md new file mode 100644 index 0000000000..acc52ea16c --- /dev/null +++ b/avm/res/container-registry/registry/credential-set/README.md @@ -0,0 +1,124 @@ +# Container Registries Credential Sets `[Microsoft.ContainerRegistry/registries/credentialSets]` + +This module deploys an ACR Credential Set. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ContainerRegistry/registries/credentialSets` | [2023-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/credentialSets) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`authCredentials`](#parameter-authcredentials) | array | List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential. | +| [`loginServer`](#parameter-loginserver) | string | The credentials are stored for this upstream or login server. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`name`](#parameter-name) | string | The name of the credential set. | +| [`registryName`](#parameter-registryname) | string | The name of the parent registry. Required if the template is used in a standalone deployment. | + +### Parameter: `authCredentials` + +List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-authcredentialsname) | string | The name of the credential. | +| [`passwordSecretIdentifier`](#parameter-authcredentialspasswordsecretidentifier) | string | KeyVault Secret URI for accessing the password. | +| [`usernameSecretIdentifier`](#parameter-authcredentialsusernamesecretidentifier) | string | KeyVault Secret URI for accessing the username. | + +### Parameter: `authCredentials.name` + +The name of the credential. + +- Required: Yes +- Type: string + +### Parameter: `authCredentials.passwordSecretIdentifier` + +KeyVault Secret URI for accessing the password. + +- Required: Yes +- Type: string + +### Parameter: `authCredentials.usernameSecretIdentifier` + +KeyVault Secret URI for accessing the username. + +- Required: Yes +- Type: string + +### Parameter: `loginServer` + +The credentials are stored for this upstream or login server. + +- Required: Yes +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: Yes +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `name` + +The name of the credential set. + +- Required: Yes +- Type: string + +### Parameter: `registryName` + +The name of the parent registry. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The Name of the Credential Set. | +| `resourceGroupName` | string | The name of the Credential Set. | +| `resourceId` | string | The resource ID of the Credential Set. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/container-registry/registry/credential-set/main.bicep b/avm/res/container-registry/registry/credential-set/main.bicep new file mode 100644 index 0000000000..8618151043 --- /dev/null +++ b/avm/res/container-registry/registry/credential-set/main.bicep @@ -0,0 +1,70 @@ +metadata name = 'Container Registries Credential Sets' +metadata description = 'This module deploys an ACR Credential Set.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the parent registry. Required if the template is used in a standalone deployment.') +param registryName string + +@description('Required. The name of the credential set.') +param name string + +@description('Required. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential.') +param authCredentials authCredentialsType + +@description('Required. The credentials are stored for this upstream or login server.') +param loginServer string + +var identity = !empty(managedIdentities) + ? { + type: (managedIdentities.?systemAssigned ?? false) ? 'SystemAssigned' : null + } + : null + +resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' existing = { + name: registryName +} + +resource credentialSet 'Microsoft.ContainerRegistry/registries/credentialSets@2023-11-01-preview' = { + name: name + parent: registry + identity: identity + properties: { + authCredentials: authCredentials + loginServer: loginServer + } +} + +@description('The Name of the Credential Set.') +output name string = credentialSet.name + +@description('The name of the Credential Set.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Credential Set.') +output resourceId string = credentialSet.id + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = credentialSet.?identity.?principalId ?? '' + +// =============== // +// Definitions // +// =============== // + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? +} + +type authCredentialsType = { + @description('Required. The name of the credential.') + name: string + + @description('Required. KeyVault Secret URI for accessing the username.') + usernameSecretIdentifier: string + + @description('Required. KeyVault Secret URI for accessing the password.') + passwordSecretIdentifier: string +}[] diff --git a/avm/res/container-registry/registry/credential-set/main.json b/avm/res/container-registry/registry/credential-set/main.json new file mode 100644 index 0000000000..381d007948 --- /dev/null +++ b/avm/res/container-registry/registry/credential-set/main.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12196074162662855376" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + } + }, + "authCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + } + } + } + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Required. The managed identity definition for this resource." + } + }, + "authCredentials": { + "$ref": "#/definitions/authCredentialsType", + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Credential Set." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Credential Set." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId'), '')]" + } + } +} \ No newline at end of file diff --git a/avm/res/container-registry/registry/main.bicep b/avm/res/container-registry/registry/main.bicep index 886ba39d1f..200b1efaf7 100644 --- a/avm/res/container-registry/registry/main.bicep +++ b/avm/res/container-registry/registry/main.bicep @@ -136,9 +136,15 @@ param anonymousPullEnabled bool = false @description('Optional. The customer managed key definition.') param customerManagedKey customerManagedKeyType -@description('Optional. Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview)).') +@description('Optional. Array of Cache Rules.') param cacheRules array? +@description('Optional. Array of Credential Sets.') +param credentialSets array = [] + +@description('Optional. Scope maps setting.') +param scopeMaps scopeMapsType + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -183,6 +189,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.containerregistry-registry.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -292,6 +309,18 @@ resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' = } } +module registry_scopeMaps 'scope-map/main.bicep' = [ + for (scopeMap, index) in (scopeMaps ?? []): { + name: '${uniqueString(deployment().name, location)}-Registry-Scope-${index}' + params: { + name: scopeMap.?name + actions: scopeMap.actions + description: scopeMap.?description + registryName: registry.name + } + } +] + module registry_replications 'replication/main.bicep' = [ for (replication, index) in (replications ?? []): { name: '${uniqueString(deployment().name, location)}-Registry-Replication-${index}' @@ -306,7 +335,20 @@ module registry_replications 'replication/main.bicep' = [ } ] -module registry_cacheRules 'cache-rules/main.bicep' = [ +module registry_credentialSets 'credential-set/main.bicep' = [ + for (credentialSet, index) in (credentialSets ?? []): { + name: '${uniqueString(deployment().name, location)}-Registry-CredentialSet-${index}' + params: { + name: credentialSet.name + registryName: registry.name + managedIdentities: credentialSet.managedIdentities + authCredentials: credentialSet.authCredentials + loginServer: credentialSet.loginServer + } + } +] + +module registry_cacheRules 'cache-rule/main.bicep' = [ for (cacheRule, index) in (cacheRules ?? []): { name: '${uniqueString(deployment().name, location)}-Registry-Cache-${index}' params: { @@ -316,6 +358,9 @@ module registry_cacheRules 'cache-rules/main.bicep' = [ targetRepository: cacheRule.?targetRepository ?? cacheRule.sourceRepository credentialSetResourceId: cacheRule.?credentialSetResourceId } + dependsOn: [ + registry_credentialSets + ] } ] @@ -383,14 +428,10 @@ resource registry_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021 ] resource registry_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(registry.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(registry.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -473,6 +514,16 @@ output systemAssignedMIPrincipalId string = registry.?identity.?principalId ?? ' @description('The location the resource was deployed into.') output location string = registry.location +@description('The Principal IDs of the ACR Credential Sets system-assigned identities.') +output credentialSetsSystemAssignedMIPrincipalIds array = [ + for index in range(0, length(credentialSets)): registry_credentialSets[index].outputs.systemAssignedMIPrincipalId +] + +@description('The Resource IDs of the ACR Credential Sets.') +output credentialSetsResourceIds array = [ + for index in range(0, length(credentialSets)): registry_credentialSets[index].outputs.resourceId +] + // =============== // // Definitions // // =============== // @@ -494,6 +545,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -651,3 +705,14 @@ type customerManagedKeyType = { @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') userAssignedIdentityResourceId: string? }? + +type scopeMapsType = { + @description('Optional. The name of the scope map.') + name: string? + + @description('Required. The list of scoped permissions for registry artifacts.') + actions: string[] + + @description('Optional. The user friendly description of the scope map.') + description: string? +}[]? diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json index 5bfc43a4d4..41d1dbdd14 100644 --- a/avm/res/container-registry/registry/main.json +++ b/avm/res/container-registry/registry/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4315733021258078545" + "version": "0.29.47.4906", + "templateHash": "7973084173813038260" }, "name": "Azure Container Registries (ACR)", "description": "This module deploys an Azure Container Registry (ACR).", @@ -66,6 +66,13 @@ "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": { @@ -475,6 +482,38 @@ } }, "nullable": true + }, + "scopeMapsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -725,11 +764,31 @@ "type": "array", "nullable": true, "metadata": { - "description": "Optional. Array of Cache Rules. Note: This is a preview feature ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache#cache-for-acr-preview))." + "description": "Optional. Array of Cache Rules." + } + }, + "credentialSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Credential Sets." + } + }, + "scopeMaps": { + "$ref": "#/definitions/scopeMapsType", + "metadata": { + "description": "Optional. Scope maps setting." } } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -893,20 +952,141 @@ "registry_roleAssignments": { "copy": { "name": "registry_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ + "registry" + ] + }, + "registry_scopeMaps": { + "copy": { + "name": "registry_scopeMaps", + "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" + }, + "actions": { + "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" + }, + "registryName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9144531012597082524" + }, + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } + } }, "dependsOn": [ "registry" @@ -952,8 +1132,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13769087012819677314" + "version": "0.29.47.4906", + "templateHash": "8531695368487734118" }, "name": "Azure Container Registry (ACR) Replications", "description": "This module deploys an Azure Container Registry (ACR) Replication.", @@ -1063,6 +1243,182 @@ "registry" ] }, + "registry_credentialSets": { + "copy": { + "name": "registry_credentialSets", + "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "managedIdentities": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" + }, + "authCredentials": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" + }, + "loginServer": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12196074162662855376" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + } + }, + "authCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + } + } + } + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Required. The managed identity definition for this resource." + } + }, + "authCredentials": { + "$ref": "#/definitions/authCredentialsType", + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Credential Set." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Credential Set." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, "registry_cacheRules": { "copy": { "name": "registry_cacheRules", @@ -1095,13 +1451,12 @@ }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9456138761658214484" + "version": "0.29.47.4906", + "templateHash": "4294329625336671928" }, "name": "Container Registries Cache", "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", @@ -1136,20 +1491,13 @@ }, "credentialSetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + "description": "Required. The resource ID of the credential store which is associated with the cache rule." } } }, - "resources": { - "registry": { - "existing": true, - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2023-06-01-preview", - "name": "[parameters('registryName')]" - }, - "cacheRule": { + "resources": [ + { "type": "Microsoft.ContainerRegistry/registries/cacheRules", "apiVersion": "2023-06-01-preview", "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", @@ -1157,12 +1505,9 @@ "sourceRepository": "[parameters('sourceRepository')]", "targetRepository": "[parameters('targetRepository')]", "credentialSetResourceId": "[parameters('credentialSetResourceId')]" - }, - "dependsOn": [ - "registry" - ] + } } - }, + ], "outputs": { "name": { "type": "string", @@ -1189,7 +1534,8 @@ } }, "dependsOn": [ - "registry" + "registry", + "registry_credentialSets" ] }, "registry_webhooks": { @@ -1241,8 +1587,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "708950816969162319" + "version": "0.29.47.4906", + "templateHash": "14912363209364245195" }, "name": "Azure Container Registry (ACR) Webhooks", "description": "This module deploys an Azure Container Registry (ACR) Webhook.", @@ -2110,6 +2456,26 @@ "description": "The location the resource was deployed into." }, "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + }, + "credentialSetsSystemAssignedMIPrincipalIds": { + "type": "array", + "metadata": { + "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.systemAssignedMIPrincipalId.value]" + } + }, + "credentialSetsResourceIds": { + "type": "array", + "metadata": { + "description": "The Resource IDs of the ACR Credential Sets." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.resourceId.value]" + } } } } \ No newline at end of file diff --git a/avm/res/container-registry/registry/replication/main.json b/avm/res/container-registry/registry/replication/main.json index b993a4d9df..d6315f34c4 100644 --- a/avm/res/container-registry/registry/replication/main.json +++ b/avm/res/container-registry/registry/replication/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17370607380629293508" + "version": "0.29.47.4906", + "templateHash": "8531695368487734118" }, "name": "Azure Container Registry (ACR) Replications", "description": "This module deploys an Azure Container Registry (ACR) Replication.", diff --git a/avm/res/container-registry/registry/scope-map/README.md b/avm/res/container-registry/registry/scope-map/README.md new file mode 100644 index 0000000000..2c1b17ed2b --- /dev/null +++ b/avm/res/container-registry/registry/scope-map/README.md @@ -0,0 +1,84 @@ +# Container Registries scopeMaps `[Microsoft.ContainerRegistry/registries/scopeMaps]` + +This module deploys an Azure Container Registry (ACR) scopeMap. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ContainerRegistry/registries/scopeMaps` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/scopeMaps) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`actions`](#parameter-actions) | array | The list of scoped permissions for registry artifacts. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`registryName`](#parameter-registryname) | string | The name of the parent registry. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-description) | string | The user friendly description of the scope map. | +| [`name`](#parameter-name) | string | The name of the scope map. | + +### Parameter: `actions` + +The list of scoped permissions for registry artifacts. + +- Required: Yes +- Type: array + +### Parameter: `registryName` + +The name of the parent registry. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `description` + +The user friendly description of the scope map. + +- Required: No +- Type: string + +### Parameter: `name` + +The name of the scope map. + +- Required: No +- Type: string +- Default: `[format('{0}-scopemaps', parameters('registryName'))]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the scope map. | +| `resourceGroupName` | string | The name of the resource group the scope map was created in. | +| `resourceId` | string | The resource ID of the scope map. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/container-registry/registry/scope-map/main.bicep b/avm/res/container-registry/registry/scope-map/main.bicep new file mode 100644 index 0000000000..8c04d25bdb --- /dev/null +++ b/avm/res/container-registry/registry/scope-map/main.bicep @@ -0,0 +1,37 @@ +metadata name = 'Container Registries scopeMaps' +metadata description = 'This module deploys an Azure Container Registry (ACR) scopeMap.' +metadata owner = 'Azure/module-maintainers' + +@sys.description('Conditional. The name of the parent registry. Required if the template is used in a standalone deployment.') +param registryName string + +@sys.description('Optional. The name of the scope map.') +param name string = '${registryName}-scopemaps' + +@sys.description('Required. The list of scoped permissions for registry artifacts.') +param actions string[] + +@sys.description('Optional. The user friendly description of the scope map.') +param description string? + +resource registry 'Microsoft.ContainerRegistry/registries@2023-06-01-preview' existing = { + name: registryName +} + +resource scopeMap 'Microsoft.ContainerRegistry/registries/scopeMaps@2023-06-01-preview' = { + name: name + parent: registry + properties: { + actions: actions + description: description + } +} + +@sys.description('The name of the scope map.') +output name string = scopeMap.name + +@sys.description('The name of the resource group the scope map was created in.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The resource ID of the scope map.') +output resourceId string = scopeMap.id diff --git a/avm/res/container-registry/registry/scope-map/main.json b/avm/res/container-registry/registry/scope-map/main.json new file mode 100644 index 0000000000..bd25f89561 --- /dev/null +++ b/avm/res/container-registry/registry/scope-map/main.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9144531012597082524" + }, + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } +} \ No newline at end of file diff --git a/avm/res/container-registry/registry/tests/e2e/cache/dependencies.bicep b/avm/res/container-registry/registry/tests/e2e/cache/dependencies.bicep new file mode 100644 index 0000000000..00cee9eca4 --- /dev/null +++ b/avm/res/container-registry/registry/tests/e2e/cache/dependencies.bicep @@ -0,0 +1,97 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Azure Container Registry to pre-create before the actual test.') +param acrName string + +@description('Required. The name of the Key Vault referenced by the ACR Credential Set.') +param keyVaultName string + +@description('Optional. UserName secret used by the ACR Credential Set deployment. The value is a GUID.') +@secure() +param userNameSecret string = newGuid() + +@description('Optional. Password secret used by the ACR Credential Set deployment. The value is a GUID.') +@secure() +param passwordSecret string = newGuid() + +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: subscription().tenantId + publicNetworkAccess: 'Enabled' + enableRbacAuthorization: true + } +} + +resource keyVaultSecretUserName 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + parent: keyVault + name: 'UserName' + properties: { + value: userNameSecret + } +} + +resource keyVaulSecretPwd 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + parent: keyVault + name: 'Password' + properties: { + value: passwordSecret + } +} + +resource acr 'Microsoft.ContainerRegistry/registries@2023-11-01-preview' = { + name: acrName + location: location + sku: { + name: 'Standard' + } +} + +resource acrCredentialSet 'Microsoft.ContainerRegistry/registries/credentialSets@2023-11-01-preview' = { + parent: acr + name: 'default' + identity: { + type: 'SystemAssigned' + } + properties: { + authCredentials: [ + { + name: 'Credential1' + passwordSecretIdentifier: keyVaulSecretPwd.properties.secretUri + usernameSecretIdentifier: keyVaultSecretUserName.properties.secretUri + } + ] + loginServer: 'docker.io' + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${acrCredentialSet.name}-KeyVaultSecretUser-RoleAssignment') + scope: keyVault + properties: { + principalId: acrCredentialSet.identity.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4633458b-17de-408a-b874-0445c86b69e6' + ) // Key Vault Secrets User + principalType: 'ServicePrincipal' + } +} + +@description('The username key vault secret URI.') +output userNameSecretURI string = keyVaultSecretUserName.properties.secretUri + +@description('The password key vault secret URI.') +output pwdSecretURI string = keyVaulSecretPwd.properties.secretUri + +@description('The name of the Azure Container Registry.') +output acrName string = acr.name + +@description('The resource ID of the Azure Container Registry Credential Set.') +output acrCredentialSetResourceId string = acrCredentialSet.id diff --git a/avm/res/container-registry/registry/tests/e2e/cache/main.test.bicep b/avm/res/container-registry/registry/tests/e2e/cache/main.test.bicep new file mode 100644 index 0000000000..c19abf87f1 --- /dev/null +++ b/avm/res/container-registry/registry/tests/e2e/cache/main.test.bicep @@ -0,0 +1,85 @@ +targetScope = 'subscription' + +metadata name = 'Using cache rules' +metadata description = 'This instance deploys the module with a credential set and a cache rule.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-containerregistry.registries-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'crrcach' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + location: resourceLocation + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + acrName: '${namePrefix}${serviceShort}001' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: nestedDependencies.outputs.acrName + location: resourceLocation + acrAdminUserEnabled: false + acrSku: 'Standard' + credentialSets: [ + { + name: 'default' + managedIdentities: { + systemAssigned: true + } + authCredentials: [ + { + name: 'Credential1' + usernameSecretIdentifier: nestedDependencies.outputs.userNameSecretURI + passwordSecretIdentifier: nestedDependencies.outputs.pwdSecretURI + } + ] + loginServer: 'docker.io' + } + ] + cacheRules: [ + { + name: 'customRule' + sourceRepository: 'docker.io/library/hello-world' + targetRepository: 'cached-docker-hub/hello-world' + credentialSetResourceId: nestedDependencies.outputs.acrCredentialSetResourceId + } + ] + } + } +] diff --git a/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep b/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep index e724da8735..8865621415 100644 --- a/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep +++ b/avm/res/container-registry/registry/tests/e2e/max/main.test.bicep @@ -127,11 +127,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '60395919-cfd3-47bf-8349-775ddebb255e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -152,16 +154,6 @@ module testDeployment '../../../main.bicep' = [ ] } trustPolicyStatus: 'enabled' - cacheRules: [ - { - name: 'customRule' - sourceRepository: 'docker.io/library/hello-world' - targetRepository: 'cached-docker-hub/hello-world' - } - { - sourceRepository: 'docker.io/library/hello-world' - } - ] webhooks: [ { name: '${namePrefix}acrx001webhook' diff --git a/avm/res/container-registry/registry/tests/e2e/scope/main.test.bicep b/avm/res/container-registry/registry/tests/e2e/scope/main.test.bicep new file mode 100644 index 0000000000..0fa23ae7ec --- /dev/null +++ b/avm/res/container-registry/registry/tests/e2e/scope/main.test.bicep @@ -0,0 +1,58 @@ +targetScope = 'subscription' + +metadata name = 'Using `scopeMaps` in parameter set' +metadata description = 'This instance deploys the module with the scopeMaps feature.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-containerregistry.registries-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'crrs' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + acrSku: 'Standard' + scopeMaps: [ + { + name: 'testscopemap' + actions: [ + 'repositories/*/content/read' + ] + description: 'This is a test for scopeMaps feature.' + } + ] + } + } +] diff --git a/avm/res/container-registry/registry/version.json b/avm/res/container-registry/registry/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/container-registry/registry/version.json +++ b/avm/res/container-registry/registry/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/container-registry/registry/webhook/main.json b/avm/res/container-registry/registry/webhook/main.json index 928fb03b03..66adb6a4cf 100644 --- a/avm/res/container-registry/registry/webhook/main.json +++ b/avm/res/container-registry/registry/webhook/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12261942841024526503" + "version": "0.29.47.4906", + "templateHash": "14912363209364245195" }, "name": "Azure Container Registry (ACR) Webhooks", "description": "This module deploys an Azure Container Registry (ACR) Webhook.", diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index d381a5ff29..9af4601434 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -19,6 +19,7 @@ This module deploys an Azure Kubernetes Service (AKS) Managed Cluster. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.ContainerService/managedClusters` | [2024-03-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2024-03-02-preview/managedClusters) | | `Microsoft.ContainerService/managedClusters/agentPools` | [2023-07-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2023-07-02-preview/managedClusters/agentPools) | +| `Microsoft.ContainerService/managedClusters/maintenanceConfigurations` | [2023-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerService/2023-10-01/managedClusters/maintenanceConfigurations) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.KubernetesConfiguration/extensions` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KubernetesConfiguration/2022-03-01/extensions) | | `Microsoft.KubernetesConfiguration/fluxConfigurations` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KubernetesConfiguration/2022-03-01/fluxConfigurations) | @@ -31,13 +32,124 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/container-service/managed-cluster:`. -- [Using Azure CNI Network Plugin.](#example-1-using-azure-cni-network-plugin) -- [Using only defaults](#example-2-using-only-defaults) -- [Using Kubenet Network Plugin.](#example-3-using-kubenet-network-plugin) -- [Using Private Cluster.](#example-4-using-private-cluster) -- [WAF-aligned](#example-5-waf-aligned) +- [Using only defaults and use AKS Automatic mode](#example-1-using-only-defaults-and-use-aks-automatic-mode) +- [Using Azure CNI Network Plugin.](#example-2-using-azure-cni-network-plugin) +- [Using only defaults](#example-3-using-only-defaults) +- [Using Kubenet Network Plugin.](#example-4-using-kubenet-network-plugin) +- [Using Private Cluster.](#example-5-using-private-cluster) +- [WAF-aligned](#example-6-waf-aligned) -### Example 1: _Using Azure CNI Network Plugin._ +### Example 1: _Using only defaults and use AKS Automatic mode_ + +This instance deploys the module with the set of automatic parameters. + + +

+ +via Bicep module + +```bicep +module managedCluster 'br/public:avm/res/container-service/managed-cluster:' = { + name: 'managedClusterDeployment' + params: { + // Required parameters + name: 'csauto001' + primaryAgentPoolProfile: [ + { + count: 3 + mode: 'System' + name: 'systempool' + vmSize: 'Standard_DS2_v2' + } + ] + // Non-required parameters + location: '' + maintenanceConfiguration: { + maintenanceWindow: { + durationHours: 4 + schedule: { + absoluteMonthly: '' + daily: '' + relativeMonthly: '' + weekly: { + dayOfWeek: 'Sunday' + intervalWeeks: 1 + } + } + startDate: '2024-07-03' + startTime: '00:00' + utcOffset: '+00:00' + } + } + managedIdentities: { + systemAssigned: true + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "csauto001" + }, + "primaryAgentPoolProfile": { + "value": [ + { + "count": 3, + "mode": "System", + "name": "systempool", + "vmSize": "Standard_DS2_v2" + } + ] + }, + // Non-required parameters + "location": { + "value": "" + }, + "maintenanceConfiguration": { + "value": { + "maintenanceWindow": { + "durationHours": 4, + "schedule": { + "absoluteMonthly": "", + "daily": "", + "relativeMonthly": "", + "weekly": { + "dayOfWeek": "Sunday", + "intervalWeeks": 1 + } + }, + "startDate": "2024-07-03", + "startTime": "00:00", + "utcOffset": "+00:00" + } + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + } + } +} +``` + +
+

+ +### Example 2: _Using Azure CNI Network Plugin._ This instance deploys the module with Azure CNI network plugin . @@ -232,11 +344,13 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -507,11 +621,13 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -537,7 +653,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:

-### Example 2: _Using only defaults_ +### Example 3: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -611,7 +727,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:

-### Example 3: _Using Kubenet Network Plugin._ +### Example 4: _Using Kubenet Network Plugin._ This instance deploys the module with Kubenet network plugin . @@ -713,11 +829,13 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -853,11 +971,13 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -883,7 +1003,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:

-### Example 4: _Using Private Cluster._ +### Example 5: _Using Private Cluster._ This instance deploys the module with a private cluster instance. @@ -1101,7 +1221,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:

-### Example 5: _WAF-aligned_ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Well-Architected Framework. @@ -1449,7 +1569,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/container-service/managed-cluster/maintenance-configurations/main.bicep b/avm/res/container-service/managed-cluster/maintenance-configurations/main.bicep new file mode 100644 index 0000000000..52f609f006 --- /dev/null +++ b/avm/res/container-service/managed-cluster/maintenance-configurations/main.bicep @@ -0,0 +1,33 @@ +metadata name = 'Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations' +metadata description = 'This module deploys an Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Maintenance window for the maintenance configuration.') +param maintenanceWindow object + +@description('Conditional. The name of the parent managed cluster. Required if the template is used in a standalone deployment.') +param managedClusterName string + +@description('Optional. Name of the maintenance configuration.') +param name string = 'aksManagedAutoUpgradeSchedule' + +resource managedCluster 'Microsoft.ContainerService/managedClusters@2024-03-02-preview' existing = { + name: managedClusterName +} + +resource aksManagedAutoUpgradeSchedule 'Microsoft.ContainerService/managedClusters/maintenanceConfigurations@2023-10-01' = { + name: name + parent: managedCluster + properties: { + maintenanceWindow: maintenanceWindow + } +} + +@description('The name of the maintenance configuration.') +output name string = aksManagedAutoUpgradeSchedule.name + +@description('The resource ID of the maintenance configuration.') +output resourceId string = aksManagedAutoUpgradeSchedule.id + +@description('The resource group the agent pool was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/container-service/managed-cluster/maintenance-configurations/main.json b/avm/res/container-service/managed-cluster/maintenance-configurations/main.json new file mode 100644 index 0000000000..3c4f84d104 --- /dev/null +++ b/avm/res/container-service/managed-cluster/maintenance-configurations/main.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12168542117744033419" + }, + "name": "Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations", + "description": "This module deploys an Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "maintenanceWindow": { + "type": "object", + "metadata": { + "description": "Required. Maintenance window for the maintenance configuration." + } + }, + "managedClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "aksManagedAutoUpgradeSchedule", + "metadata": { + "description": "Optional. Name of the maintenance configuration." + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/maintenanceConfigurations", + "apiVersion": "2023-10-01", + "name": "[format('{0}/{1}', parameters('managedClusterName'), parameters('name'))]", + "properties": { + "maintenanceWindow": "[parameters('maintenanceWindow')]" + } + } + ], + "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.ContainerService/managedClusters/maintenanceConfigurations', parameters('managedClusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the agent pool was deployed into." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/container-service/managed-cluster/tests/e2e/automatic/main.test.bicep b/avm/res/container-service/managed-cluster/tests/e2e/automatic/main.test.bicep new file mode 100644 index 0000000000..5d8d63a837 --- /dev/null +++ b/avm/res/container-service/managed-cluster/tests/e2e/automatic/main.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults and use AKS Automatic mode' +metadata description = 'This instance deploys the module with the set of automatic parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-containerservice.managedclusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csauto' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + maintenanceConfiguration: { + maintenanceWindow: { + schedule: { + daily: null + weekly: { + intervalWeeks: 1 + dayOfWeek: 'Sunday' + } + absoluteMonthly: null + relativeMonthly: null + } + durationHours: 4 + utcOffset: '+00:00' + startDate: '2024-07-03' + startTime: '00:00' + } + } + managedIdentities: { + systemAssigned: true + } + + primaryAgentPoolProfile: [ + { + name: 'systempool' + count: 3 + vmSize: 'Standard_DS2_v2' + mode: 'System' + } + ] + } + } +] diff --git a/avm/res/container-service/managed-cluster/tests/e2e/azure/main.test.bicep b/avm/res/container-service/managed-cluster/tests/e2e/azure/main.test.bicep index 5bd3d88063..9032ea2840 100644 --- a/avm/res/container-service/managed-cluster/tests/e2e/azure/main.test.bicep +++ b/avm/res/container-service/managed-cluster/tests/e2e/azure/main.test.bicep @@ -196,11 +196,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'ac915208-669e-4665-9792-7e2dc861f569' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/container-service/managed-cluster/tests/e2e/kubenet/main.test.bicep b/avm/res/container-service/managed-cluster/tests/e2e/kubenet/main.test.bicep index 9400ca0c57..6b5171e708 100644 --- a/avm/res/container-service/managed-cluster/tests/e2e/kubenet/main.test.bicep +++ b/avm/res/container-service/managed-cluster/tests/e2e/kubenet/main.test.bicep @@ -147,11 +147,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '6acf186b-abbd-491b-8bd7-39fa199da81e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/container-service/managed-cluster/version.json b/avm/res/container-service/managed-cluster/version.json index 8def869ede..76049e1c4a 100644 --- a/avm/res/container-service/managed-cluster/version.json +++ b/avm/res/container-service/managed-cluster/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/data-factory/factory/README.md b/avm/res/data-factory/factory/README.md index 68259c4e1e..470fbd183e 100644 --- a/avm/res/data-factory/factory/README.md +++ b/avm/res/data-factory/factory/README.md @@ -24,8 +24,8 @@ This module deploys a Data Factory. | `Microsoft.DataFactory/factories/managedVirtualNetworks` | [2018-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks) | | `Microsoft.DataFactory/factories/managedVirtualNetworks/managedPrivateEndpoints` | [2018-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DataFactory/2018-06-01/factories/managedVirtualNetworks/managedPrivateEndpoints) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -193,9 +193,13 @@ module factory 'br/public:avm/res/data-factory/factory:' = { managedVirtualNetworkName: 'default' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { application: 'AVM' @@ -203,19 +207,25 @@ module factory 'br/public:avm/res/data-factory/factory:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: '12093237-f40a-4f36-868f-accbeebf540c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -365,9 +375,13 @@ module factory 'br/public:avm/res/data-factory/factory:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "application": "AVM", @@ -375,9 +389,13 @@ module factory 'br/public:avm/res/data-factory/factory:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -385,11 +403,13 @@ module factory 'br/public:avm/res/data-factory/factory:' = { "roleAssignments": { "value": [ { + "name": "12093237-f40a-4f36-868f-accbeebf540c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -988,8 +1008,7 @@ Configuration Details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1173,19 +1192,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1223,6 +1287,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1273,6 +1338,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1342,6 +1414,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1392,6 +1465,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1423,8 +1503,9 @@ Tags of the resource. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The Name of the Azure Data Factory instance. | +| `privateEndpoints` | array | The private endpoints of the Data Factory. | | `resourceGroupName` | string | The name of the Resource Group with the Data factory. | -| `resourceId` | string | The Resource ID of the Data factory. | +| `resourceId` | string | The Resource ID of the Data Factory. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | ## Cross-referenced modules @@ -1433,7 +1514,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Notes diff --git a/avm/res/data-factory/factory/integration-runtime/main.json b/avm/res/data-factory/factory/integration-runtime/main.json index 40ba307631..991ffcaaa3 100644 --- a/avm/res/data-factory/factory/integration-runtime/main.json +++ b/avm/res/data-factory/factory/integration-runtime/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17668999863735154468" + "version": "0.29.47.4906", + "templateHash": "7050455062079134223" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", diff --git a/avm/res/data-factory/factory/linked-service/main.json b/avm/res/data-factory/factory/linked-service/main.json index c66b5b07a6..c44e38f1ed 100644 --- a/avm/res/data-factory/factory/linked-service/main.json +++ b/avm/res/data-factory/factory/linked-service/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6177259720192070721" + "version": "0.29.47.4906", + "templateHash": "2964661488202892260" }, "name": "Data Factory Linked Service", "description": "This module deploys a Data Factory Linked Service.", diff --git a/avm/res/data-factory/factory/main.bicep b/avm/res/data-factory/factory/main.bicep index 57d184e8df..bf2a338511 100644 --- a/avm/res/data-factory/factory/main.bicep +++ b/avm/res/data-factory/factory/main.bicep @@ -121,6 +121,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) scope: resourceGroup( @@ -292,14 +303,10 @@ resource dataFactory_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2 ] resource dataFactory_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dataFactory.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(dataFactory.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -311,7 +318,7 @@ resource dataFactory_roleAssignments 'Microsoft.Authorization/roleAssignments@20 } ] -module dataFactory_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module dataFactory_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-dataFactory-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -352,8 +359,7 @@ module dataFactory_privateEndpoints 'br/public:avm/res/network/private-endpoint: 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -367,7 +373,7 @@ module dataFactory_privateEndpoints 'br/public:avm/res/network/private-endpoint: @description('The Name of the Azure Data Factory instance.') output name string = dataFactory.name -@description('The Resource ID of the Data factory.') +@description('The Resource ID of the Data Factory.') output resourceId string = dataFactory.id @description('The name of the Resource Group with the Data factory.') @@ -379,6 +385,17 @@ output systemAssignedMIPrincipalId string = dataFactory.?identity.?principalId ? @description('The location the resource was deployed into.') output location string = dataFactory.location +@description('The private endpoints of the Data Factory.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: dataFactory_privateEndpoints[i].outputs.name + resourceId: dataFactory_privateEndpoints[i].outputs.resourceId + groupId: dataFactory_privateEndpoints[i].outputs.groupId + customDnsConfig: dataFactory_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: dataFactory_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -400,6 +417,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -438,11 +458,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/data-factory/factory/main.json b/avm/res/data-factory/factory/main.json index 2718e401bb..74ba78d45e 100644 --- a/avm/res/data-factory/factory/main.json +++ b/avm/res/data-factory/factory/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14871862405495060994" + "version": "0.29.47.4906", + "templateHash": "577014275789719005" }, "name": "Data Factories", "description": "This module deploys a Data Factory.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -667,6 +697,13 @@ } }, "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": { @@ -805,20 +842,20 @@ "dataFactory_roleAssignments": { "copy": { "name": "dataFactory_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DataFactory/factories/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DataFactory/factories', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DataFactory/factories', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dataFactory" @@ -851,8 +888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17394540019744867695" + "version": "0.29.47.4906", + "templateHash": "17839923462414788715" }, "name": "Data Factory Managed Virtual Networks", "description": "This module deploys a Data Factory Managed Virtual Network.", @@ -925,8 +962,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1848819997653095971" + "version": "0.29.47.4906", + "templateHash": "3694105160445163406" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", @@ -1073,8 +1110,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17668999863735154468" + "version": "0.29.47.4906", + "templateHash": "7050455062079134223" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", @@ -1211,8 +1248,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6177259720192070721" + "version": "0.29.47.4906", + "templateHash": "2964661488202892260" }, "name": "Data Factory Linked Service", "description": "This module deploys a Data Factory Linked Service.", @@ -1342,11 +1379,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1374,19 +1408,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1490,13 +1552,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1530,8 +1592,11 @@ "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." + "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": { @@ -1571,8 +1636,11 @@ "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." + "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": { @@ -1620,6 +1688,29 @@ } }, "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": { @@ -1655,18 +1746,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1722,6 +1806,13 @@ } }, "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')]", @@ -1739,8 +1830,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1758,7 +1849,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1799,27 +1890,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1830,28 +1921,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1859,12 +1974,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1878,27 +1996,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1956,14 +2083,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1984,7 +2125,7 @@ "resourceId": { "type": "string", "metadata": { - "description": "The Resource ID of the Data factory." + "description": "The Resource ID of the Data Factory." }, "value": "[resourceId('Microsoft.DataFactory/factories', parameters('name'))]" }, @@ -2008,6 +2149,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('dataFactory', '2018-06-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Data Factory." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('dataFactory_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('dataFactory_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('dataFactory_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('dataFactory_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('dataFactory_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/data-factory/factory/managed-virtual-network/main.json b/avm/res/data-factory/factory/managed-virtual-network/main.json index 8c324b4ebd..f84cc1629b 100644 --- a/avm/res/data-factory/factory/managed-virtual-network/main.json +++ b/avm/res/data-factory/factory/managed-virtual-network/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17394540019744867695" + "version": "0.29.47.4906", + "templateHash": "17839923462414788715" }, "name": "Data Factory Managed Virtual Networks", "description": "This module deploys a Data Factory Managed Virtual Network.", @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1848819997653095971" + "version": "0.29.47.4906", + "templateHash": "3694105160445163406" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", diff --git a/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json b/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json index 1c420cd44d..28c3cf639b 100644 --- a/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json +++ b/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1848819997653095971" + "version": "0.29.47.4906", + "templateHash": "3694105160445163406" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", diff --git a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep index 9dae35b49c..03a1f38a74 100644 --- a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep +++ b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep @@ -157,9 +157,13 @@ module testDeployment '../../../main.bicep' = [ managedVirtualNetworkName: 'default' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -167,19 +171,25 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] roleAssignments: [ { + name: '12093237-f40a-4f36-868f-accbeebf540c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/data-factory/factory/version.json b/avm/res/data-factory/factory/version.json index c177b1bb58..a8eda31021 100644 --- a/avm/res/data-factory/factory/version.json +++ b/avm/res/data-factory/factory/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/data-protection/backup-vault/README.md b/avm/res/data-protection/backup-vault/README.md index f4ec188a97..40942f73a8 100644 --- a/avm/res/data-protection/backup-vault/README.md +++ b/avm/res/data-protection/backup-vault/README.md @@ -170,11 +170,13 @@ module backupVault 'br/public:avm/res/data-protection/backup-vault:' = } roleAssignments: [ { + name: 'cbc3932a-1bee-4318-ae76-d70e1ba399c8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -295,11 +297,13 @@ module backupVault 'br/public:avm/res/data-protection/backup-vault:' = "roleAssignments": { "value": [ { + "name": "cbc3932a-1bee-4318-ae76-d70e1ba399c8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -707,6 +711,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -757,6 +762,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/data-protection/backup-vault/backup-policy/main.json b/avm/res/data-protection/backup-vault/backup-policy/main.json index 4590e10872..e3acf37db2 100644 --- a/avm/res/data-protection/backup-vault/backup-policy/main.json +++ b/avm/res/data-protection/backup-vault/backup-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11424915933898928003" + "version": "0.29.47.4906", + "templateHash": "11474624425108890813" }, "name": "Data Protection Backup Vault Backup Policies", "description": "This module deploys a Data Protection Backup Vault Backup Policy.", diff --git a/avm/res/data-protection/backup-vault/main.bicep b/avm/res/data-protection/backup-vault/main.bicep index 632e5e5db7..823bef6550 100644 --- a/avm/res/data-protection/backup-vault/main.bicep +++ b/avm/res/data-protection/backup-vault/main.bicep @@ -87,6 +87,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.dataprotection-backupvault.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -151,14 +162,10 @@ resource backupVault_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empt } resource backupVault_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(backupVault.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(backupVault.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -203,6 +210,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/data-protection/backup-vault/main.json b/avm/res/data-protection/backup-vault/main.json index 18afd64d57..8b6bfa5a98 100644 --- a/avm/res/data-protection/backup-vault/main.json +++ b/avm/res/data-protection/backup-vault/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2272096113073237939" + "version": "0.29.47.4906", + "templateHash": "2218732823006005750" }, "name": "Data Protection Backup Vaults", "description": "This module deploys a Data Protection Backup Vault.", @@ -56,6 +56,13 @@ "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": { @@ -222,6 +229,13 @@ } }, "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)))))]" + } + ], "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]", "builtInRoleNames": { "Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b')]", @@ -295,20 +309,20 @@ "backupVault_roleAssignments": { "copy": { "name": "backupVault_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DataProtection/backupVaults/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DataProtection/backupVaults', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DataProtection/backupVaults', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "backupVault" @@ -344,8 +358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7087640411790284650" + "version": "0.29.47.4906", + "templateHash": "11474624425108890813" }, "name": "Data Protection Backup Vault Backup Policies", "description": "This module deploys a Data Protection Backup Vault Backup Policy.", diff --git a/avm/res/data-protection/backup-vault/tests/e2e/max/main.test.bicep b/avm/res/data-protection/backup-vault/tests/e2e/max/main.test.bicep index dea4591b1c..6b799e1995 100644 --- a/avm/res/data-protection/backup-vault/tests/e2e/max/main.test.bicep +++ b/avm/res/data-protection/backup-vault/tests/e2e/max/main.test.bicep @@ -54,11 +54,13 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation roleAssignments: [ { + name: 'cbc3932a-1bee-4318-ae76-d70e1ba399c8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/data-protection/backup-vault/version.json b/avm/res/data-protection/backup-vault/version.json index 96236a61ba..a8eda31021 100644 --- a/avm/res/data-protection/backup-vault/version.json +++ b/avm/res/data-protection/backup-vault/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/databricks/access-connector/README.md b/avm/res/databricks/access-connector/README.md index eab286a37c..89d7b82894 100644 --- a/avm/res/databricks/access-connector/README.md +++ b/avm/res/databricks/access-connector/README.md @@ -108,11 +108,13 @@ module accessConnector 'br/public:avm/res/databricks/access-connector:' } roleAssignments: [ { + name: 'e9143a6b-a031-419c-a597-cc4ac9bd39ed' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -169,11 +171,13 @@ module accessConnector 'br/public:avm/res/databricks/access-connector:' "roleAssignments": { "value": [ { + "name": "e9143a6b-a031-419c-a597-cc4ac9bd39ed", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -406,6 +410,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -456,6 +461,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/databricks/access-connector/main.bicep b/avm/res/databricks/access-connector/main.bicep index 99572ff749..7d6bd8b452 100644 --- a/avm/res/databricks/access-connector/main.bicep +++ b/avm/res/databricks/access-connector/main.bicep @@ -54,6 +54,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.databricks-accessconnector.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -93,14 +104,10 @@ resource accessConnector_lock 'Microsoft.Authorization/locks@2020-05-01' = if (! } resource accessConnector_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(accessConnector.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(accessConnector.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -148,6 +155,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/databricks/access-connector/main.json b/avm/res/databricks/access-connector/main.json index 461b532576..26fac7709a 100644 --- a/avm/res/databricks/access-connector/main.json +++ b/avm/res/databricks/access-connector/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16192649159638972948" + "version": "0.29.47.4906", + "templateHash": "5967998875388507317" }, "name": "Azure Databricks Access Connectors", "description": "This module deploys an Azure Databricks Access Connector.", @@ -66,6 +66,13 @@ "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": { @@ -176,6 +183,13 @@ } }, "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())), createObject('type', 'None'))]", "builtInRoleNames": { @@ -233,20 +247,20 @@ "accessConnector_roleAssignments": { "copy": { "name": "accessConnector_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Databricks/accessConnectors/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Databricks/accessConnectors', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Databricks/accessConnectors', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "accessConnector" diff --git a/avm/res/databricks/access-connector/tests/e2e/max/main.test.bicep b/avm/res/databricks/access-connector/tests/e2e/max/main.test.bicep index ac2fb53b02..66536c8a4f 100644 --- a/avm/res/databricks/access-connector/tests/e2e/max/main.test.bicep +++ b/avm/res/databricks/access-connector/tests/e2e/max/main.test.bicep @@ -63,11 +63,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'e9143a6b-a031-419c-a597-cc4ac9bd39ed' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/databricks/access-connector/version.json b/avm/res/databricks/access-connector/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/databricks/access-connector/version.json +++ b/avm/res/databricks/access-connector/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/databricks/workspace/README.md b/avm/res/databricks/workspace/README.md index e9a01e2beb..20941b4932 100644 --- a/avm/res/databricks/workspace/README.md +++ b/avm/res/databricks/workspace/README.md @@ -18,10 +18,10 @@ This module deploys an Azure Databricks Workspace. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Databricks/workspaces` | [2023-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Databricks/2023-02-01/workspaces) | +| `Microsoft.Databricks/workspaces` | [2024-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Databricks/2024-05-01/workspaces) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -97,7 +97,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { name: 'workspaceDeployment' params: { // Required parameters - name: 'dwmax001' + name: 'dwmax002' // Non-required parameters amlWorkspaceResourceId: '' customerManagedKey: { @@ -142,9 +142,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { prepareEncryption: true privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'databricks_ui_api' subnetResourceId: '' tags: { @@ -153,9 +157,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'browser_authentication' subnetResourceId: '' } @@ -166,11 +174,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { requireInfrastructureEncryption: true roleAssignments: [ { + name: '2754e64b-b96e-44bc-9cb2-6e39b057f515' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -208,7 +218,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "parameters": { // Required parameters "name": { - "value": "dwmax001" + "value": "dwmax002" }, // Non-required parameters "amlWorkspaceResourceId": { @@ -285,9 +295,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "databricks_ui_api", "subnetResourceId": "", "tags": { @@ -296,9 +310,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "browser_authentication", "subnetResourceId": "" } @@ -319,11 +337,13 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "roleAssignments": { "value": [ { + "name": "2754e64b-b96e-44bc-9cb2-6e39b057f515", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -377,6 +397,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { // Required parameters name: 'dwwaf001' // Non-required parameters + accessConnectorResourceId: '' amlWorkspaceResourceId: '' customerManagedKey: { keyName: '' @@ -431,12 +452,30 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { } } ] + privateStorageAccount: 'Enabled' publicIpName: 'nat-gw-public-ip' publicNetworkAccess: 'Disabled' requiredNsgRules: 'NoAzureDatabricksRules' requireInfrastructureEncryption: true skuName: 'premium' storageAccountName: 'sadwwaf001' + storageAccountPrivateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } + service: 'blob' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] storageAccountSkuName: 'Standard_ZRS' tags: { Environment: 'Non-Prod' @@ -465,6 +504,9 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "value": "dwwaf001" }, // Non-required parameters + "accessConnectorResourceId": { + "value": "" + }, "amlWorkspaceResourceId": { "value": "" }, @@ -551,6 +593,9 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { } ] }, + "privateStorageAccount": { + "value": "Enabled" + }, "publicIpName": { "value": "nat-gw-public-ip" }, @@ -569,6 +614,25 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "storageAccountName": { "value": "sadwwaf001" }, + "storageAccountPrivateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, + "service": "blob", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, "storageAccountSkuName": { "value": "Standard_ZRS" }, @@ -598,6 +662,12 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the Azure Databricks workspace to create. | +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessConnectorResourceId`](#parameter-accessconnectorresourceid) | string | The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled. | + **Optional parameters** | Parameter | Type | Description | @@ -619,6 +689,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { | [`natGatewayName`](#parameter-natgatewayname) | string | Name of the NAT gateway for Secure Cluster Connectivity (No Public IP) workspace subnets. | | [`prepareEncryption`](#parameter-prepareencryption) | bool | Prepare the workspace for encryption. Enables the Managed Identity for managed storage account. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`privateStorageAccount`](#parameter-privatestorageaccount) | string | Determines whether the managed storage account should be private or public. For best security practices, it is recommended to set it to Enabled. | | [`publicIpName`](#parameter-publicipname) | string | Name of the Public IP for No Public IP workspace with managed vNet. | | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | The network access type for accessing workspace. Set value to disabled to access workspace only via private link. | | [`requiredNsgRules`](#parameter-requirednsgrules) | string | Gets or sets a value indicating whether data plane (clusters) to control plane communication happen over private endpoint. | @@ -626,6 +697,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`skuName`](#parameter-skuname) | string | The pricing tier of workspace. | | [`storageAccountName`](#parameter-storageaccountname) | string | Default DBFS storage account name. | +| [`storageAccountPrivateEndpoints`](#parameter-storageaccountprivateendpoints) | array | Configuration details for private endpoints for the managed workspace storage account, required when privateStorageAccount is set to Enabled. For security reasons, it is recommended to use private endpoints whenever possible. | | [`storageAccountSkuName`](#parameter-storageaccountskuname) | string | Storage account SKU name. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`vnetAddressPrefix`](#parameter-vnetaddressprefix) | string | Address prefix for Managed virtual network. | @@ -637,6 +709,14 @@ The name of the Azure Databricks workspace to create. - Required: Yes - Type: string +### Parameter: `accessConnectorResourceId` + +The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `amlWorkspaceResourceId` The resource ID of a Azure Machine Learning workspace to link with Databricks workspace. @@ -1015,8 +1095,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1206,19 +1285,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1256,6 +1380,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1306,6 +1431,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1330,6 +1462,22 @@ Tags to be applied on all resources/resource groups in this deployment. - Required: No - Type: object +### Parameter: `privateStorageAccount` + +Determines whether the managed storage account should be private or public. For best security practices, it is recommended to set it to Enabled. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'Disabled' + 'Enabled' + ] + ``` + ### Parameter: `publicIpName` Name of the Public IP for No Public IP workspace with managed vNet. @@ -1398,6 +1546,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1448,6 +1597,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1489,6 +1645,401 @@ Default DBFS storage account name. - Type: string - Default: `''` +### Parameter: `storageAccountPrivateEndpoints` + +Configuration details for private endpoints for the managed workspace storage account, required when privateStorageAccount is set to Enabled. For security reasons, it is recommended to use private endpoints whenever possible. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`service`](#parameter-storageaccountprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". | +| [`subnetResourceId`](#parameter-storageaccountprivateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationSecurityGroupResourceIds`](#parameter-storageaccountprivateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`customDnsConfigs`](#parameter-storageaccountprivateendpointscustomdnsconfigs) | array | Custom DNS configurations. | +| [`customNetworkInterfaceName`](#parameter-storageaccountprivateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | +| [`enableTelemetry`](#parameter-storageaccountprivateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`ipConfigurations`](#parameter-storageaccountprivateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-storageaccountprivateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | +| [`location`](#parameter-storageaccountprivateendpointslocation) | string | The location to deploy the private endpoint to. | +| [`lock`](#parameter-storageaccountprivateendpointslock) | object | Specify the type of lock. | +| [`manualConnectionRequestMessage`](#parameter-storageaccountprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | +| [`name`](#parameter-storageaccountprivateendpointsname) | string | The name of the private endpoint. | +| [`privateDnsZoneGroup`](#parameter-storageaccountprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`privateLinkServiceConnectionName`](#parameter-storageaccountprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | +| [`resourceGroupName`](#parameter-storageaccountprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | +| [`roleAssignments`](#parameter-storageaccountprivateendpointsroleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-storageaccountprivateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | + +### Parameter: `storageAccountPrivateEndpoints.service` + +The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.subnetResourceId` + +Resource ID of the subnet where the endpoint needs to be created. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.applicationSecurityGroupResourceIds` + +Application security groups in which the private endpoint IP configuration is included. + +- Required: No +- Type: array + +### Parameter: `storageAccountPrivateEndpoints.customDnsConfigs` + +Custom DNS configurations. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`fqdn`](#parameter-storageaccountprivateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-storageaccountprivateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | + +### Parameter: `storageAccountPrivateEndpoints.customDnsConfigs.fqdn` + +Fqdn that resolves to private endpoint IP address. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.customDnsConfigs.ipAddresses` + +A list of private IP addresses of the private endpoint. + +- Required: Yes +- Type: array + +### Parameter: `storageAccountPrivateEndpoints.customNetworkInterfaceName` + +The custom name of the network interface attached to the private endpoint. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations` + +A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageaccountprivateendpointsipconfigurationsname) | string | The name of the resource that is unique within a resource group. | +| [`properties`](#parameter-storageaccountprivateendpointsipconfigurationsproperties) | object | Properties of private endpoint IP configurations. | + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations.name` + +The name of the resource that is unique within a resource group. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations.properties` + +Properties of private endpoint IP configurations. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupId`](#parameter-storageaccountprivateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | +| [`memberName`](#parameter-storageaccountprivateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | +| [`privateIPAddress`](#parameter-storageaccountprivateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations.properties.groupId` + +The ID of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations.properties.memberName` + +The member name of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.ipConfigurations.properties.privateIPAddress` + +A private IP address obtained from the private endpoint's subnet. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + +### Parameter: `storageAccountPrivateEndpoints.location` + +The location to deploy the private endpoint to. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.lock` + +Specify the type of lock. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-storageaccountprivateendpointslockkind) | string | Specify the type of lock. | +| [`name`](#parameter-storageaccountprivateendpointslockname) | string | Specify the name of lock. | + +### Parameter: `storageAccountPrivateEndpoints.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `storageAccountPrivateEndpoints.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.manualConnectionRequestMessage` + +A message passed to the owner of the remote resource with the manual connection request. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.name` + +The name of the private endpoint. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-storageaccountprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageaccountprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `storageAccountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-storageaccountprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageaccountprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `storageAccountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-storageaccountprivateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-storageaccountprivateendpointsroleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-storageaccountprivateendpointsroleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-storageaccountprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-storageaccountprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-storageaccountprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-storageaccountprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-storageaccountprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `storageAccountPrivateEndpoints.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `storageAccountPrivateEndpoints.tags` + +Tags to be applied on all resources/resource groups in this deployment. + +- Required: No +- Type: object + ### Parameter: `storageAccountSkuName` Storage account SKU name. @@ -1521,11 +2072,12 @@ Address prefix for Managed virtual network. | `managedResourceGroupId` | string | The resource ID of the managed resource group. | | `managedResourceGroupName` | string | The name of the managed resource group. | | `name` | string | The name of the deployed databricks workspace. | -| `privateEndpoints` | array | The private endpoints for the Databricks Workspace. | +| `privateEndpoints` | array | The private endpoints of the Databricks Workspace. | | `resourceGroupName` | string | The resource group of the deployed databricks workspace. | | `resourceId` | string | The resource ID of the deployed databricks workspace. | | `storageAccountId` | string | The resource ID of the DBFS storage account. | | `storageAccountName` | string | The name of the DBFS storage account. | +| `storagePrivateEndpoints` | array | The private endpoints of the Databricks Workspace Storage. | | `workspaceId` | string | The unique identifier of the databricks workspace in databricks control plane. | | `workspaceUrl` | string | The workspace URL which is of the format 'adb-{workspaceId}.{random}.azuredatabricks.net'. | @@ -1535,7 +2087,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Notes diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index 236f3b14d9..a0d1bd4b59 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -96,9 +96,23 @@ param publicNetworkAccess string = 'Enabled' ]) param requiredNsgRules string = 'AllRules' +@description('Optional. Determines whether the managed storage account should be private or public. For best security practices, it is recommended to set it to Enabled.') +@allowed([ + 'Enabled' + 'Disabled' + '' +]) +param privateStorageAccount string = '' + @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType +@description('Optional. Configuration details for private endpoints for the managed workspace storage account, required when privateStorageAccount is set to Enabled. For security reasons, it is recommended to use private endpoints whenever possible.') +param storageAccountPrivateEndpoints privateEndpointType + +@description('Conditional. The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled.') +param accessConnectorResourceId string = '' + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') @@ -113,6 +127,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.databricks-workspace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -156,143 +181,155 @@ resource cMKManagedDiskKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing } } -resource workspace 'Microsoft.Databricks/workspaces@2023-02-01' = { +resource workspace 'Microsoft.Databricks/workspaces@2024-05-01' = { name: name location: location tags: tags sku: { name: skuName } - properties: { - managedResourceGroupId: !empty(managedResourceGroupResourceId) - ? managedResourceGroupResourceId - : '${subscription().id}/resourceGroups/rg-${name}-managed' - parameters: union( - // Always added parameters - { - enableNoPublicIp: { - value: disablePublicIp - } - prepareEncryption: { - value: prepareEncryption - } - vnetAddressPrefix: { - value: vnetAddressPrefix - } - requireInfrastructureEncryption: { - value: requireInfrastructureEncryption - } - }, - // Parameters only added if not empty - !empty(customVirtualNetworkResourceId) - ? { - customVirtualNetworkId: { - value: customVirtualNetworkResourceId - } + // This union is required because the defaultStorageFirewall property is optional and cannot be null or '' + properties: union( + { + managedResourceGroupId: !empty(managedResourceGroupResourceId) + ? managedResourceGroupResourceId + : '${subscription().id}/resourceGroups/rg-${name}-managed' + parameters: union( + // Always added parameters + { + enableNoPublicIp: { + value: disablePublicIp } - : {}, - !empty(amlWorkspaceResourceId) - ? { - amlWorkspaceId: { - value: amlWorkspaceResourceId - } + prepareEncryption: { + value: prepareEncryption } - : {}, - !empty(customPrivateSubnetName) - ? { - customPrivateSubnetName: { - value: customPrivateSubnetName - } + vnetAddressPrefix: { + value: vnetAddressPrefix } - : {}, - !empty(customPublicSubnetName) - ? { - customPublicSubnetName: { - value: customPublicSubnetName - } + requireInfrastructureEncryption: { + value: requireInfrastructureEncryption } - : {}, - !empty(loadBalancerBackendPoolName) - ? { - loadBalancerBackendPoolName: { - value: loadBalancerBackendPoolName + }, + // Parameters only added if not empty + !empty(customVirtualNetworkResourceId) + ? { + customVirtualNetworkId: { + value: customVirtualNetworkResourceId + } } - } - : {}, - !empty(loadBalancerResourceId) - ? { - loadBalancerId: { - value: loadBalancerResourceId + : {}, + !empty(amlWorkspaceResourceId) + ? { + amlWorkspaceId: { + value: amlWorkspaceResourceId + } } - } - : {}, - !empty(natGatewayName) - ? { - natGatewayName: { - value: natGatewayName + : {}, + !empty(customPrivateSubnetName) + ? { + customPrivateSubnetName: { + value: customPrivateSubnetName + } } - } - : {}, - !empty(publicIpName) - ? { - publicIpName: { - value: publicIpName + : {}, + !empty(customPublicSubnetName) + ? { + customPublicSubnetName: { + value: customPublicSubnetName + } } - } - : {}, - !empty(storageAccountName) - ? { - storageAccountName: { - value: storageAccountName + : {}, + !empty(loadBalancerBackendPoolName) + ? { + loadBalancerBackendPoolName: { + value: loadBalancerBackendPoolName + } } - } - : {}, - !empty(storageAccountSkuName) + : {}, + !empty(loadBalancerResourceId) + ? { + loadBalancerId: { + value: loadBalancerResourceId + } + } + : {}, + !empty(natGatewayName) + ? { + natGatewayName: { + value: natGatewayName + } + } + : {}, + !empty(publicIpName) + ? { + publicIpName: { + value: publicIpName + } + } + : {}, + !empty(storageAccountName) + ? { + storageAccountName: { + value: storageAccountName + } + } + : {}, + !empty(storageAccountSkuName) + ? { + storageAccountSkuName: { + value: storageAccountSkuName + } + } + : {} + ) + // createdBy: {} // This is a read-only property + // managedDiskIdentity: {} // This is a read-only property + // storageAccountIdentity: {} // This is a read-only property + // updatedBy: {} // This is a read-only property + publicNetworkAccess: publicNetworkAccess + requiredNsgRules: requiredNsgRules + encryption: !empty(customerManagedKey) || !empty(customerManagedKeyManagedDisk) ? { - storageAccountSkuName: { - value: storageAccountSkuName + entities: { + managedServices: !empty(customerManagedKey) + ? { + keySource: 'Microsoft.Keyvault' + keyVaultProperties: { + keyVaultUri: cMKKeyVault.properties.vaultUri + keyName: customerManagedKey!.keyName + keyVersion: !empty(customerManagedKey.?keyVersion ?? '') + ? customerManagedKey!.keyVersion! + : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + } + } + : null + managedDisk: !empty(customerManagedKeyManagedDisk) + ? { + keySource: 'Microsoft.Keyvault' + keyVaultProperties: { + keyVaultUri: cMKManagedDiskKeyVault.properties.vaultUri + keyName: customerManagedKeyManagedDisk!.keyName + keyVersion: !empty(customerManagedKeyManagedDisk.?keyVersion ?? '') + ? customerManagedKeyManagedDisk!.keyVersion! + : last(split(cMKManagedDiskKeyVault::cMKKey.properties.keyUriWithVersion, '/')) + } + rotationToLatestKeyVersionEnabled: customerManagedKeyManagedDisk.?rotationToLatestKeyVersionEnabled ?? true + } + : null } } - : {} - ) - // createdBy: {} // This is a read-only property - // managedDiskIdentity: {} // This is a read-only property - // storageAccountIdentity: {} // This is a read-only property - // updatedBy: {} // This is a read-only property - publicNetworkAccess: publicNetworkAccess - requiredNsgRules: requiredNsgRules - encryption: !empty(customerManagedKey) || !empty(customerManagedKeyManagedDisk) + : null + }, + !empty(privateStorageAccount) ? { - entities: { - managedServices: !empty(customerManagedKey) - ? { - keySource: 'Microsoft.Keyvault' - keyVaultProperties: { - keyVaultUri: cMKKeyVault.properties.vaultUri - keyName: customerManagedKey!.keyName - keyVersion: !empty(customerManagedKey.?keyVersion ?? '') - ? customerManagedKey!.keyVersion! - : last(split(cMKKeyVault::cMKKey.properties.keyUriWithVersion, '/')) - } - } - : null - managedDisk: !empty(customerManagedKeyManagedDisk) - ? { - keySource: 'Microsoft.Keyvault' - keyVaultProperties: { - keyVaultUri: cMKManagedDiskKeyVault.properties.vaultUri - keyName: customerManagedKeyManagedDisk!.keyName - keyVersion: !empty(customerManagedKeyManagedDisk.?keyVersion ?? '') - ? customerManagedKeyManagedDisk!.keyVersion! - : last(split(cMKManagedDiskKeyVault::cMKKey.properties.keyUriWithVersion, '/')) - } - rotationToLatestKeyVersionEnabled: customerManagedKeyManagedDisk.?rotationToLatestKeyVersionEnabled ?? true - } - : null + defaultStorageFirewall: privateStorageAccount + accessConnector: { + id: accessConnectorResourceId + identityType: 'SystemAssigned' } } - : null - } + : {} + ) } resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { @@ -330,14 +367,10 @@ resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 ] resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -350,7 +383,7 @@ resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 ] @batchSize(1) -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -391,8 +424,68 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup + roleAssignments: privateEndpoint.?roleAssignments + tags: privateEndpoint.?tags ?? tags + customDnsConfigs: privateEndpoint.?customDnsConfigs + ipConfigurations: privateEndpoint.?ipConfigurations + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName + } + } +] + +// To reuse at multiple places instead to repeat the same code +var _storageAccountName = workspace.properties.parameters.storageAccountName.value +var _storageAccountId = resourceId( + last(split(workspace.properties.managedResourceGroupId, '/')), + 'microsoft.storage/storageAccounts', + workspace.properties.parameters.storageAccountName.value +) + +@batchSize(1) +module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ + for (privateEndpoint, index) in (storageAccountPrivateEndpoints ?? []): if (privateStorageAccount == 'Enabled') { + name: '${uniqueString(deployment().name, location)}-workspacestorage-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + params: { + name: privateEndpoint.?name ?? 'pep-${_storageAccountName}-${privateEndpoint.service}-${index}' + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${_storageAccountName}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: _storageAccountId + groupIds: [ + privateEndpoint.service + ] + } + } + ] + : null + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${_storageAccountName}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: _storageAccountId + groupIds: [ + privateEndpoint.service + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] + : null + subnetResourceId: privateEndpoint.subnetResourceId + enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry + 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 @@ -422,14 +515,10 @@ output managedResourceGroupId string = workspace.properties.managedResourceGroup output managedResourceGroupName string = last(split(workspace.properties.managedResourceGroupId, '/')) @description('The name of the DBFS storage account.') -output storageAccountName string = workspace.properties.parameters.storageAccountName.value +output storageAccountName string = _storageAccountName @description('The resource ID of the DBFS storage account.') -output storageAccountId string = resourceId( - last(split(workspace.properties.managedResourceGroupId, '/')), - 'microsoft.storage/storageAccounts', - workspace.properties.parameters.storageAccountName.value -) +output storageAccountId string = _storageAccountId @description('The workspace URL which is of the format \'adb-{workspaceId}.{random}.azuredatabricks.net\'.') output workspaceUrl string = workspace.properties.workspaceUrl @@ -437,11 +526,27 @@ output workspaceUrl string = workspace.properties.workspaceUrl @description('The unique identifier of the databricks workspace in databricks control plane.') output workspaceId string = workspace.properties.workspaceId -@description('The private endpoints for the Databricks Workspace.') +@description('The private endpoints of the Databricks Workspace.') output privateEndpoints array = [ for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { name: workspace_privateEndpoints[i].outputs.name resourceId: workspace_privateEndpoints[i].outputs.resourceId + groupId: workspace_privateEndpoints[i].outputs.groupId + customDnsConfig: workspace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: workspace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The private endpoints of the Databricks Workspace Storage.') +output storagePrivateEndpoints array = [ + for (pe, i) in ((!empty(storageAccountPrivateEndpoints) && privateStorageAccount == 'Enabled') + ? array(storageAccountPrivateEndpoints) + : []): { + name: storageAccount_privateEndpoints[i].outputs.name + resourceId: storageAccount_privateEndpoints[i].outputs.resourceId + groupId: storageAccount_privateEndpoints[i].outputs.groupId + customDnsConfig: storageAccount_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: storageAccount_privateEndpoints[i].outputs.networkInterfaceIds } ] @@ -473,11 +578,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? + + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @@ -567,6 +681,9 @@ type customerManagedKeyManagedDiskType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index 05ea98f9ba..b30f600d96 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2761051872895400818" + "version": "0.29.47.4906", + "templateHash": "13865252037974484038" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace.", @@ -76,21 +76,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -310,6 +333,13 @@ "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": { @@ -664,14 +694,46 @@ "description": "Optional. Gets or sets a value indicating whether data plane (clusters) to control plane communication happen over private endpoint." } }, + "privateStorageAccount": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Enabled", + "Disabled", + "" + ], + "metadata": { + "description": "Optional. Determines whether the managed storage account should be private or public. For best security practices, it is recommended to set it to Enabled." + } + }, "privateEndpoints": { "$ref": "#/definitions/privateEndpointType", "metadata": { "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } + }, + "storageAccountPrivateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints for the managed workspace storage account, required when privateStorageAccount is set to Enabled. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "accessConnectorResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled." + } } }, "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')]", @@ -745,20 +807,14 @@ }, "workspace": { "type": "Microsoft.Databricks/workspaces", - "apiVersion": "2023-02-01", + "apiVersion": "2024-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "sku": { "name": "[parameters('skuName')]" }, - "properties": { - "managedResourceGroupId": "[if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name')))]", - "parameters": "[union(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject()))]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "requiredNsgRules": "[parameters('requiredNsgRules')]", - "encryption": "[if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), parameters('customerManagedKeyManagedDisk').keyVersion, last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'rotationToLatestKeyVersionEnabled'), true())), null()))), null())]" - }, + "properties": "[union(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', union(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject())), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), parameters('customerManagedKeyManagedDisk').keyVersion, last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'rotationToLatestKeyVersionEnabled'), true())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()))]", "dependsOn": [ "cMKKeyVault", "cMKManagedDiskKeyVault" @@ -813,20 +869,20 @@ "workspace_roleAssignments": { "copy": { "name": "workspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Databricks/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Databricks/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Databricks/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "workspace" @@ -866,11 +922,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -898,19 +951,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1014,13 +1095,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1054,8 +1135,11 @@ "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." + "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": { @@ -1095,8 +1179,11 @@ "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." + "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": { @@ -1144,6 +1231,29 @@ } }, "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": { @@ -1179,18 +1289,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1246,6 +1349,13 @@ } }, "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')]", @@ -1263,8 +1373,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1282,7 +1392,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1323,27 +1433,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1354,28 +1464,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1383,12 +1517,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1402,27 +1539,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1480,14 +1626,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1495,89 +1655,877 @@ "dependsOn": [ "workspace" ] - } - }, - "outputs": { - "name": { - "type": "string", - "metadata": { - "description": "The name of the deployed databricks workspace." - }, - "value": "[parameters('name')]" - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the deployed databricks workspace." - }, - "value": "[resourceId('Microsoft.Databricks/workspaces', parameters('name'))]" - }, - "resourceGroupName": { - "type": "string", - "metadata": { - "description": "The resource group of the deployed databricks workspace." - }, - "value": "[resourceGroup().name]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('workspace', '2023-02-01', 'full').location]" - }, - "managedResourceGroupId": { - "type": "string", - "metadata": { - "description": "The resource ID of the managed resource group." - }, - "value": "[reference('workspace').managedResourceGroupId]" - }, - "managedResourceGroupName": { - "type": "string", - "metadata": { - "description": "The name of the managed resource group." - }, - "value": "[last(split(reference('workspace').managedResourceGroupId, '/'))]" - }, - "storageAccountName": { - "type": "string", - "metadata": { - "description": "The name of the DBFS storage account." - }, - "value": "[reference('workspace').parameters.storageAccountName.value]" - }, - "storageAccountId": { - "type": "string", - "metadata": { - "description": "The resource ID of the DBFS storage account." - }, - "value": "[resourceId(last(split(reference('workspace').managedResourceGroupId, '/')), 'microsoft.storage/storageAccounts', reference('workspace').parameters.storageAccountName.value)]" - }, - "workspaceUrl": { - "type": "string", - "metadata": { - "description": "The workspace URL which is of the format 'adb-{workspaceId}.{random}.azuredatabricks.net'." - }, - "value": "[reference('workspace').workspaceUrl]" - }, - "workspaceId": { - "type": "string", - "metadata": { - "description": "The unique identifier of the databricks workspace in databricks control plane." - }, - "value": "[reference('workspace').workspaceId]" }, - "privateEndpoints": { - "type": "array", - "metadata": { - "description": "The private endpoints for the Databricks Workspace." - }, + "storageAccount_privateEndpoints": { "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", - "input": { - "name": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]" + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('storageAccountPrivateEndpoints'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[equals(parameters('privateStorageAccount'), 'Enabled')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspacestorage-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', reference('workspace').parameters.storageAccountName.value, coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', reference('workspace').parameters.storageAccountName.value, coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId(last(split(reference('workspace').managedResourceGroupId, '/')), 'microsoft.storage/storageAccounts', reference('workspace').parameters.storageAccountName.value), 'groupIds', createArray(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', reference('workspace').parameters.storageAccountName.value, coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId(last(split(reference('workspace').managedResourceGroupId, '/')), 'microsoft.storage/storageAccounts', reference('workspace').parameters.storageAccountName.value), 'groupIds', createArray(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('storageAccountPrivateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "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": [ + "workspace" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed databricks workspace." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed databricks workspace." + }, + "value": "[resourceId('Microsoft.Databricks/workspaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed databricks workspace." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('workspace', '2024-05-01', 'full').location]" + }, + "managedResourceGroupId": { + "type": "string", + "metadata": { + "description": "The resource ID of the managed resource group." + }, + "value": "[reference('workspace').managedResourceGroupId]" + }, + "managedResourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the managed resource group." + }, + "value": "[last(split(reference('workspace').managedResourceGroupId, '/'))]" + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "The name of the DBFS storage account." + }, + "value": "[reference('workspace').parameters.storageAccountName.value]" + }, + "storageAccountId": { + "type": "string", + "metadata": { + "description": "The resource ID of the DBFS storage account." + }, + "value": "[resourceId(last(split(reference('workspace').managedResourceGroupId, '/')), 'microsoft.storage/storageAccounts', reference('workspace').parameters.storageAccountName.value)]" + }, + "workspaceUrl": { + "type": "string", + "metadata": { + "description": "The workspace URL which is of the format 'adb-{workspaceId}.{random}.azuredatabricks.net'." + }, + "value": "[reference('workspace').workspaceUrl]" + }, + "workspaceId": { + "type": "string", + "metadata": { + "description": "The unique identifier of the databricks workspace in databricks control plane." + }, + "value": "[reference('workspace').workspaceId]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Databricks Workspace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "storagePrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Databricks Workspace Storage." + }, + "copy": { + "count": "[length(if(and(not(empty(parameters('storageAccountPrivateEndpoints'))), equals(parameters('privateStorageAccount'), 'Enabled')), array(parameters('storageAccountPrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" } } } diff --git a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep index 76d138190e..9405b82ff3 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep @@ -406,3 +406,6 @@ output keyVaultDiskKeyName string = keyVaultDisk::key.name @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name diff --git a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep index a607df924c..b1745bfe1d 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep @@ -23,6 +23,10 @@ param baseTime string = utcNow('u') @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The object id of the AzureDatabricks Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDatabricksEnterpriseApplicationObjectId\'.') +@secure() +param azureDatabricksEnterpriseApplicationObjectId string = '' + // ============ // // Dependencies // // ============ // @@ -47,7 +51,7 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}' virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' - databricksApplicationObjectId: '711330f9-cfad-4b10-a462-d82faa92027d' // Tenant-specific 'AzureDatabricks' Enterprise Application Object Id + databricksApplicationObjectId: azureDatabricksEnterpriseApplicationObjectId keyVaultDiskName: 'dep-${namePrefix}-kve-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' @@ -61,7 +65,7 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}diasa${serviceShort}' - logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + logAnalyticsWorkspaceName: nestedDependencies.outputs.logAnalyticsWorkspaceName eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' location: resourceLocation @@ -78,7 +82,7 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - name: '${namePrefix}${serviceShort}001' + name: '${namePrefix}${serviceShort}002' location: resourceLocation diagnosticSettings: [ { @@ -103,11 +107,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '2754e64b-b96e-44bc-9cb2-6e39b057f515' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -152,9 +158,13 @@ module testDeployment '../../../main.bicep' = [ customVirtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'databricks_ui_api' subnetResourceId: nestedDependencies.outputs.primarySubnetResourceId tags: { @@ -163,9 +173,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.secondarySubnetResourceId service: 'browser_authentication' } diff --git a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep index 0f3406899f..6a67b9fd10 100644 --- a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -34,6 +34,9 @@ param virtualNetworkName string @description('Required. The object ID of the Databricks Enterprise Application. Required for Customer-Managed-Keys.') param databricksApplicationObjectId string +@description('Required. The name of the Access Connector to create.') +param accessConnectorName string + var addressPrefix = '10.0.0.0/16' resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { @@ -359,6 +362,31 @@ resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { } } +resource blobStoragePrivateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.blob.${environment().suffixes.storage}' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource accessConnector 'Microsoft.Databricks/accessConnectors@2024-05-01' = { + name: accessConnectorName + location: location + identity: { + type: 'SystemAssigned' + } + properties: {} +} + @description('The resource ID of the created Virtual Network Default Subnet.') output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id @@ -371,9 +399,12 @@ output customPrivateSubnetName string = virtualNetwork.properties.subnets[2].nam @description('The resource ID of the created Virtual Network.') output virtualNetworkResourceId string = virtualNetwork.id -@description('The resource ID of the created Private DNS Zone.') +@description('The resource ID of the created Private DNS Zone for databricks.') output privateDNSZoneResourceId string = privateDNSZone.id +@description('The resource ID of the created Private DNS Zone for blob storage.') +output blobStoragePrivateDNSZoneResourceId string = blobStoragePrivateDNSZone.id + @description('The resource ID of the created Azure Machine Learning Workspace.') output machineLearningWorkspaceResourceId string = machineLearningWorkspace.id @@ -397,3 +428,9 @@ output keyVaultDiskKeyName string = keyVaultDisk::key.name @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The resource ID of the created access connector.') +output accessConnectorResourceId string = accessConnector.id diff --git a/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep index 908a07ea2e..1602f41f85 100644 --- a/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -23,6 +23,10 @@ param baseTime string = utcNow('u') @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The object id of the AzureDatabricks Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDatabricksEnterpriseApplicationObjectId\'.') +@secure() +param azureDatabricksEnterpriseApplicationObjectId string = '' + // ============ // // Dependencies // // ============ // @@ -47,10 +51,11 @@ module nestedDependencies 'dependencies.bicep' = { storageAccountName: 'dep${namePrefix}sa${serviceShort}' virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' - databricksApplicationObjectId: '711330f9-cfad-4b10-a462-d82faa92027d' // Tenant-specific 'AzureDatabricks' Enterprise Application Object Id + databricksApplicationObjectId: azureDatabricksEnterpriseApplicationObjectId keyVaultDiskName: 'dep-${namePrefix}-kve-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + accessConnectorName: 'dep-${namePrefix}-ac-${serviceShort}' } } @@ -61,7 +66,7 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}diasa${serviceShort}' - logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + logAnalyticsWorkspaceName: nestedDependencies.outputs.logAnalyticsWorkspaceName eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' location: resourceLocation @@ -147,6 +152,25 @@ module testDeployment '../../../main.bicep' = [ managedResourceGroupResourceId: '${subscription().id}/resourceGroups/rg-${resourceGroupName}-managed' requireInfrastructureEncryption: true vnetAddressPrefix: '10.100' + privateStorageAccount: 'Enabled' + accessConnectorResourceId: nestedDependencies.outputs.accessConnectorResourceId + storageAccountPrivateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.blobStoragePrivateDNSZoneResourceId + } + ] + } + service: 'blob' + subnetResourceId: nestedDependencies.outputs.defaultSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] } dependsOn: [ nestedDependencies diff --git a/avm/res/databricks/workspace/version.json b/avm/res/databricks/workspace/version.json index a8eda31021..7e1d3f4157 100644 --- a/avm/res/databricks/workspace/version.json +++ b/avm/res/databricks/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/db-for-my-sql/flexible-server/README.md b/avm/res/db-for-my-sql/flexible-server/README.md index eab033d768..e28b5db4f8 100644 --- a/avm/res/db-for-my-sql/flexible-server/README.md +++ b/avm/res/db-for-my-sql/flexible-server/README.md @@ -17,10 +17,10 @@ This module deploys a DBforMySQL Flexible Server. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.DBforMySQL/flexibleServers` | [2022-09-30-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-09-30-preview/flexibleServers) | -| `Microsoft.DBforMySQL/flexibleServers/administrators` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/administrators) | -| `Microsoft.DBforMySQL/flexibleServers/databases` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/databases) | -| `Microsoft.DBforMySQL/flexibleServers/firewallRules` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/firewallRules) | +| `Microsoft.DBforMySQL/flexibleServers` | [2023-12-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/flexibleServers) | +| `Microsoft.DBforMySQL/flexibleServers/administrators` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/administrators) | +| `Microsoft.DBforMySQL/flexibleServers/databases` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/databases) | +| `Microsoft.DBforMySQL/flexibleServers/firewallRules` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/firewallRules) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | ## Usage examples @@ -51,12 +51,13 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: params: { // Required parameters name: 'dfmsfsmin001' - skuName: 'Standard_B1ms' - tier: 'Burstable' + skuName: 'Standard_D2ds_v4' + tier: 'GeneralPurpose' // Non-required parameters administratorLogin: 'adminUserName' administratorLoginPassword: '' location: '' + storageAutoGrow: 'Enabled' } } ``` @@ -78,10 +79,10 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "value": "dfmsfsmin001" }, "skuName": { - "value": "Standard_B1ms" + "value": "Standard_D2ds_v4" }, "tier": { - "value": "Burstable" + "value": "GeneralPurpose" }, // Non-required parameters "administratorLogin": { @@ -92,6 +93,9 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: }, "location": { "value": "" + }, + "storageAutoGrow": { + "value": "Enabled" } } } @@ -174,7 +178,7 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: } ] geoRedundantBackup: 'Enabled' - highAvailability: 'SameZone' + highAvailability: 'ZoneRedundant' location: '' lock: { kind: 'CanNotDelete' @@ -188,11 +192,13 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: } roleAssignments: [ { + name: '2478b63b-0cae-457f-9bd3-9feb00e1925b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -317,7 +323,7 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "value": "Enabled" }, "highAvailability": { - "value": "SameZone" + "value": "ZoneRedundant" }, "location": { "value": "" @@ -339,11 +345,13 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "roleAssignments": { "value": [ { + "name": "2478b63b-0cae-457f-9bd3-9feb00e1925b", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -535,16 +543,20 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: params: { // Required parameters name: 'dfmswaf001' - skuName: 'Standard_B1ms' - tier: 'Burstable' + skuName: 'Standard_D2ds_v4' + tier: 'GeneralPurpose' // Non-required parameters administratorLogin: 'adminUserName' administratorLoginPassword: '' + availabilityZone: '1' + highAvailability: 'ZoneRedundant' + highAvailabilityZone: '2' location: '' lock: { kind: 'CanNotDelete' name: 'myCustomLockName' } + storageAutoGrow: 'Enabled' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -571,10 +583,10 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "value": "dfmswaf001" }, "skuName": { - "value": "Standard_B1ms" + "value": "Standard_D2ds_v4" }, "tier": { - "value": "Burstable" + "value": "GeneralPurpose" }, // Non-required parameters "administratorLogin": { @@ -583,6 +595,15 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "administratorLoginPassword": { "value": "" }, + "availabilityZone": { + "value": "1" + }, + "highAvailability": { + "value": "ZoneRedundant" + }, + "highAvailabilityZone": { + "value": "2" + }, "location": { "value": "" }, @@ -592,6 +613,9 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: "name": "myCustomLockName" } }, + "storageAutoGrow": { + "value": "Enabled" + }, "tags": { "value": { "Environment": "Non-Prod", @@ -646,6 +670,7 @@ module flexibleServer 'br/public:avm/res/db-for-my-sql/flexible-server: | [`firewallRules`](#parameter-firewallrules) | array | The firewall rules to create in the MySQL flexible server. | | [`geoRedundantBackup`](#parameter-georedundantbackup) | string | A value indicating whether Geo-Redundant backup is enabled on the server. If "Enabled" and "cMKKeyName" is not empty, then "geoBackupCMKKeyVaultResourceId" and "cMKUserAssignedIdentityResourceId" are also required. | | [`highAvailability`](#parameter-highavailability) | string | The mode for High Availability (HA). It is not supported for the Burstable pricing tier and Zone redundant HA can only be set during server provisioning. | +| [`highAvailabilityZone`](#parameter-highavailabilityzone) | string | Standby availability zone information of the server. Default will have no preference set. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`maintenanceWindow`](#parameter-maintenancewindow) | object | Properties for the maintenence window. If provided, "customWindow" property must exist and set to "Enabled". | @@ -1108,7 +1133,7 @@ The mode for High Availability (HA). It is not supported for the Burstable prici - Required: No - Type: string -- Default: `'Disabled'` +- Default: `'ZoneRedundant'` - Allowed: ```Bicep [ @@ -1118,6 +1143,14 @@ The mode for High Availability (HA). It is not supported for the Burstable prici ] ``` +### Parameter: `highAvailabilityZone` + +Standby availability zone information of the server. Default will have no preference set. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `location` Location for all resources. @@ -1208,6 +1241,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1258,6 +1292,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1335,7 +1376,7 @@ MySQL Server version. - Required: No - Type: string -- Default: `'5.7'` +- Default: `'8.0.21'` - Allowed: ```Bicep [ @@ -1349,6 +1390,7 @@ MySQL Server version. | Output | Type | Description | | :-- | :-- | :-- | +| `fqdn` | string | The FQDN of the MySQL Flexible server. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the deployed MySQL Flexible server. | | `resourceGroupName` | string | The resource group of the deployed MySQL Flexible server. | diff --git a/avm/res/db-for-my-sql/flexible-server/administrator/README.md b/avm/res/db-for-my-sql/flexible-server/administrator/README.md index ffe1d5f9cd..aa3aa4deaa 100644 --- a/avm/res/db-for-my-sql/flexible-server/administrator/README.md +++ b/avm/res/db-for-my-sql/flexible-server/administrator/README.md @@ -14,7 +14,7 @@ This module deploys a DBforMySQL Flexible Server Administrator. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.DBforMySQL/flexibleServers/administrators` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/administrators) | +| `Microsoft.DBforMySQL/flexibleServers/administrators` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/administrators) | ## Parameters diff --git a/avm/res/db-for-my-sql/flexible-server/administrator/main.bicep b/avm/res/db-for-my-sql/flexible-server/administrator/main.bicep index 912d7cbdcd..8897bdb7f6 100644 --- a/avm/res/db-for-my-sql/flexible-server/administrator/main.bicep +++ b/avm/res/db-for-my-sql/flexible-server/administrator/main.bicep @@ -17,11 +17,11 @@ param login string @description('Optional. The tenantId of the Active Directory administrator.') param tenantId string = tenant().tenantId -resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-01-01' existing = { +resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2023-12-30' existing = { name: flexibleServerName } -resource administrator 'Microsoft.DBforMySQL/flexibleServers/administrators@2022-01-01' = { +resource administrator 'Microsoft.DBforMySQL/flexibleServers/administrators@2023-06-30' = { name: 'ActiveDirectory' parent: flexibleServer properties: { diff --git a/avm/res/db-for-my-sql/flexible-server/administrator/main.json b/avm/res/db-for-my-sql/flexible-server/administrator/main.json index e4df947daa..a08e31dfad 100644 --- a/avm/res/db-for-my-sql/flexible-server/administrator/main.json +++ b/avm/res/db-for-my-sql/flexible-server/administrator/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2817201491275445304" + "version": "0.29.47.4906", + "templateHash": "166868353165081250" }, "name": "DBforMySQL Flexible Server Administrators", "description": "This module deploys a DBforMySQL Flexible Server Administrator.", @@ -47,7 +47,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/administrators", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), 'ActiveDirectory')]", "properties": { "administratorType": "ActiveDirectory", diff --git a/avm/res/db-for-my-sql/flexible-server/database/README.md b/avm/res/db-for-my-sql/flexible-server/database/README.md index d4fe4f68fb..8242a0bd31 100644 --- a/avm/res/db-for-my-sql/flexible-server/database/README.md +++ b/avm/res/db-for-my-sql/flexible-server/database/README.md @@ -14,7 +14,7 @@ This module deploys a DBforMySQL Flexible Server Database. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.DBforMySQL/flexibleServers/databases` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/databases) | +| `Microsoft.DBforMySQL/flexibleServers/databases` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/databases) | ## Parameters diff --git a/avm/res/db-for-my-sql/flexible-server/database/main.bicep b/avm/res/db-for-my-sql/flexible-server/database/main.bicep index 51fdfed6ca..f939ae6354 100644 --- a/avm/res/db-for-my-sql/flexible-server/database/main.bicep +++ b/avm/res/db-for-my-sql/flexible-server/database/main.bicep @@ -14,11 +14,11 @@ param collation string = 'utf8' @description('Optional. The charset of the database.') param charset string = 'utf8_general_ci' -resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' existing = { +resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2023-12-30' existing = { name: flexibleServerName } -resource database 'Microsoft.DBforMySQL/flexibleServers/databases@2022-01-01' = { +resource database 'Microsoft.DBforMySQL/flexibleServers/databases@2023-06-30' = { name: name parent: flexibleServer properties: { diff --git a/avm/res/db-for-my-sql/flexible-server/database/main.json b/avm/res/db-for-my-sql/flexible-server/database/main.json index 77895b3ecb..3ecb2da053 100644 --- a/avm/res/db-for-my-sql/flexible-server/database/main.json +++ b/avm/res/db-for-my-sql/flexible-server/database/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9734848552595709363" + "version": "0.29.47.4906", + "templateHash": "11351866916144767840" }, "name": "DBforMySQL Flexible Server Databases", "description": "This module deploys a DBforMySQL Flexible Server Database.", @@ -42,7 +42,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/databases", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), parameters('name'))]", "properties": { "collation": "[if(not(empty(parameters('collation'))), parameters('collation'), null())]", diff --git a/avm/res/db-for-my-sql/flexible-server/firewall-rule/README.md b/avm/res/db-for-my-sql/flexible-server/firewall-rule/README.md index 082750e131..3a84a1473e 100644 --- a/avm/res/db-for-my-sql/flexible-server/firewall-rule/README.md +++ b/avm/res/db-for-my-sql/flexible-server/firewall-rule/README.md @@ -14,7 +14,7 @@ This module deploys a DBforMySQL Flexible Server Firewall Rule. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.DBforMySQL/flexibleServers/firewallRules` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2022-01-01/flexibleServers/firewallRules) | +| `Microsoft.DBforMySQL/flexibleServers/firewallRules` | [2023-06-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DBforMySQL/2023-06-30/flexibleServers/firewallRules) | ## Parameters diff --git a/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.bicep b/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.bicep index 19c077118e..bb17c1fb47 100644 --- a/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.bicep +++ b/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.bicep @@ -14,11 +14,11 @@ param endIpAddress string @description('Conditional. The name of the parent MySQL flexible server. Required if the template is used in a standalone deployment.') param flexibleServerName string -resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' existing = { +resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2023-12-30' existing = { name: flexibleServerName } -resource firewallRule 'Microsoft.DBforMySQL/flexibleServers/firewallRules@2022-01-01' = { +resource firewallRule 'Microsoft.DBforMySQL/flexibleServers/firewallRules@2023-06-30' = { name: name parent: flexibleServer properties: { diff --git a/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.json b/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.json index 70bdf3643c..06fc052a20 100644 --- a/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.json +++ b/avm/res/db-for-my-sql/flexible-server/firewall-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2931317814738693960" + "version": "0.29.47.4906", + "templateHash": "17406464450077393680" }, "name": "DBforMySQL Flexible Server Firewall Rules", "description": "This module deploys a DBforMySQL Flexible Server Firewall Rule.", @@ -40,7 +40,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/firewallRules", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), parameters('name'))]", "properties": { "endIpAddress": "[parameters('endIpAddress')]", diff --git a/avm/res/db-for-my-sql/flexible-server/main.bicep b/avm/res/db-for-my-sql/flexible-server/main.bicep index 8d46d38113..26cdb680c9 100644 --- a/avm/res/db-for-my-sql/flexible-server/main.bicep +++ b/avm/res/db-for-my-sql/flexible-server/main.bicep @@ -44,6 +44,9 @@ param tier string @description('Optional. Availability zone information of the server. Default will have no preference set.') param availabilityZone string = '' +@description('Optional. Standby availability zone information of the server. Default will have no preference set.') +param highAvailabilityZone string = '' + @minValue(1) @maxValue(35) @description('Optional. Backup retention days for the server.') @@ -80,7 +83,7 @@ param customerManagedKeyGeo customerManagedKeyType 'ZoneRedundant' ]) @description('Optional. The mode for High Availability (HA). It is not supported for the Burstable pricing tier and Zone redundant HA can only be set during server provisioning.') -param highAvailability string = 'Disabled' +param highAvailability string = 'ZoneRedundant' @description('Optional. Properties for the maintenence window. If provided, "customWindow" property must exist and set to "Enabled".') param maintenanceWindow object = {} @@ -145,7 +148,7 @@ param storageSizeGB int = 64 '8.0.21' ]) @description('Optional. MySQL Server version.') -param version string = '5.7' +param version string = '8.0.21' @description('Optional. The databases to create in the server.') param databases array = [] @@ -162,6 +165,14 @@ param diagnosticSettings diagnosticSettingType @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true +var standByAvailabilityZoneTable = { + Disabled: null + SameZone: availabilityZone + ZoneRedundant: highAvailabilityZone +} + +var standByAvailabilityZone = standByAvailabilityZoneTable[?highAvailability] + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -193,6 +204,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.dbformysql-flexibleserver.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -252,7 +274,7 @@ resource cMKGeoUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdent ) } -resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' = { +resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2023-12-30' = { name: name location: location tags: tags @@ -287,7 +309,7 @@ resource flexibleServer 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview : null highAvailability: { mode: highAvailability - standbyAvailabilityZone: highAvailability == 'SameZone' ? availabilityZone : null + standbyAvailabilityZone: standByAvailabilityZone } maintenanceWindow: !empty(maintenanceWindow) ? { @@ -328,14 +350,10 @@ resource flexibleServer_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource flexibleServer_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(flexibleServer.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(flexibleServer.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -425,6 +443,9 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = flexibleServer.location +@description('The FQDN of the MySQL Flexible server.') +output fqdn string = flexibleServer.properties.fullyQualifiedDomainName + // =============== // // Definitions // // =============== // @@ -443,6 +464,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/db-for-my-sql/flexible-server/main.json b/avm/res/db-for-my-sql/flexible-server/main.json index b82fe2edda..ce118494a1 100644 --- a/avm/res/db-for-my-sql/flexible-server/main.json +++ b/avm/res/db-for-my-sql/flexible-server/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1522380733959447255" + "version": "0.29.47.4906", + "templateHash": "12125422157581852477" }, "name": "DBforMySQL Flexible Servers", "description": "This module deploys a DBforMySQL Flexible Server.", @@ -58,6 +58,13 @@ "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": { @@ -349,6 +356,13 @@ "description": "Optional. Availability zone information of the server. Default will have no preference set." } }, + "highAvailabilityZone": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Standby availability zone information of the server. Default will have no preference set." + } + }, "backupRetentionDays": { "type": "int", "defaultValue": 7, @@ -402,7 +416,7 @@ }, "highAvailability": { "type": "string", - "defaultValue": "Disabled", + "defaultValue": "ZoneRedundant", "allowedValues": [ "Disabled", "SameZone", @@ -512,7 +526,7 @@ }, "version": { "type": "string", - "defaultValue": "5.7", + "defaultValue": "8.0.21", "allowedValues": [ "5.7", "8.0.21" @@ -556,6 +570,19 @@ } }, "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)))))]" + } + ], + "standByAvailabilityZoneTable": { + "Disabled": null, + "SameZone": "[parameters('availabilityZone')]", + "ZoneRedundant": "[parameters('highAvailabilityZone')]" + }, + "standByAvailabilityZone": "[tryGet(variables('standByAvailabilityZoneTable'), parameters('highAvailability'))]", "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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -650,7 +677,7 @@ }, "flexibleServer": { "type": "Microsoft.DBforMySQL/flexibleServers", - "apiVersion": "2022-09-30-preview", + "apiVersion": "2023-12-30", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -671,7 +698,7 @@ "dataEncryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('type', 'AzureKeyVault', 'geoBackupKeyURI', if(equals(parameters('geoRedundantBackup'), 'Enabled'), if(not(empty(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKGeoKeyVault::cMKKey').keyUri, parameters('customerManagedKeyGeo').keyVersion), reference('cMKGeoKeyVault::cMKKey').keyUriWithVersion), null()), 'geoBackupUserAssignedIdentityId', if(equals(parameters('geoRedundantBackup'), 'Enabled'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKeyGeo'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()), 'primaryKeyURI', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion), 'primaryUserAssignedIdentityId', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/')))), null())]", "highAvailability": { "mode": "[parameters('highAvailability')]", - "standbyAvailabilityZone": "[if(equals(parameters('highAvailability'), 'SameZone'), parameters('availabilityZone'), null())]" + "standbyAvailabilityZone": "[variables('standByAvailabilityZone')]" }, "maintenanceWindow": "[if(not(empty(parameters('maintenanceWindow'))), createObject('customWindow', parameters('maintenanceWindow').customWindow, 'dayOfWeek', if(equals(parameters('maintenanceWindow').customWindow, 'Enabled'), parameters('maintenanceWindow').dayOfWeek, 0), 'startHour', if(equals(parameters('maintenanceWindow').customWindow, 'Enabled'), parameters('maintenanceWindow').startHour, 0), 'startMinute', if(equals(parameters('maintenanceWindow').customWindow, 'Enabled'), parameters('maintenanceWindow').startMinute, 0)), null())]", "network": "[if(and(not(empty(parameters('delegatedSubnetResourceId'))), empty(parameters('firewallRules'))), createObject('delegatedSubnetResourceId', parameters('delegatedSubnetResourceId'), 'privateDnsZoneResourceId', parameters('privateDnsZoneResourceId')), null())]", @@ -710,20 +737,20 @@ "flexibleServer_roleAssignments": { "copy": { "name": "flexibleServer_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DBforMySQL/flexibleServers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DBforMySQL/flexibleServers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "flexibleServer" @@ -799,8 +826,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16124321045090132550" + "version": "0.29.47.4906", + "templateHash": "11351866916144767840" }, "name": "DBforMySQL Flexible Server Databases", "description": "This module deploys a DBforMySQL Flexible Server Database.", @@ -837,7 +864,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/databases", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), parameters('name'))]", "properties": { "collation": "[if(not(empty(parameters('collation'))), parameters('collation'), null())]", @@ -907,8 +934,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5174688085450924115" + "version": "0.29.47.4906", + "templateHash": "17406464450077393680" }, "name": "DBforMySQL Flexible Server Firewall Rules", "description": "This module deploys a DBforMySQL Flexible Server Firewall Rule.", @@ -943,7 +970,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/firewallRules", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), parameters('name'))]", "properties": { "endIpAddress": "[parameters('endIpAddress')]", @@ -1014,8 +1041,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1872309055548332882" + "version": "0.29.47.4906", + "templateHash": "166868353165081250" }, "name": "DBforMySQL Flexible Server Administrators", "description": "This module deploys a DBforMySQL Flexible Server Administrator.", @@ -1057,7 +1084,7 @@ "resources": [ { "type": "Microsoft.DBforMySQL/flexibleServers/administrators", - "apiVersion": "2022-01-01", + "apiVersion": "2023-06-30", "name": "[format('{0}/{1}', parameters('flexibleServerName'), 'ActiveDirectory')]", "properties": { "administratorType": "ActiveDirectory", @@ -1125,7 +1152,14 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('flexibleServer', '2022-09-30-preview', 'full').location]" + "value": "[reference('flexibleServer', '2023-12-30', 'full').location]" + }, + "fqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the MySQL Flexible server." + }, + "value": "[reference('flexibleServer').fullyQualifiedDomainName]" } } } \ No newline at end of file diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/defaults/main.test.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/defaults/main.test.bicep index 8412371c66..e116c6e6a2 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/defaults/main.test.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/defaults/main.test.bicep @@ -11,9 +11,6 @@ metadata description = 'This instance deploys the module with the minimum set of @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dbformysql.flexibleservers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'dfmsfsmin' @@ -24,6 +21,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// Pipeline is selecting random regions which dont support all cosmos features and have constraints when creating new cosmos +#disable-next-line no-hardcoded-location +var enforcedLocation = 'northeurope' + // ============ // // Dependencies // // ============ // @@ -32,7 +33,7 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -43,14 +44,15 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation administratorLogin: 'adminUserName' administratorLoginPassword: password - skuName: 'Standard_B1ms' - tier: 'Burstable' + skuName: 'Standard_D2ds_v4' + tier: 'GeneralPurpose' + storageAutoGrow: 'Enabled' } } ] diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep index 2966ac2358..7a5c266ae9 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep @@ -21,7 +21,7 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023- location: location } -resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { name: keyVaultName location: location properties: { @@ -30,8 +30,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId + softDeleteRetentionInDays: 90 // The resource provider requires a 90 day retention period for encryption. Anything less will cause the deployment to fail. Ref: https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-customer-managed-key#requirements-for-configuring-data-encryption-for-azure-database-for-mysql-flexible-server enablePurgeProtection: true // Required for encryption to work - softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true @@ -39,7 +39,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { accessPolicies: [] } - resource key 'keys@2023-02-01' = { + resource key 'keys@2023-07-01' = { name: 'keyEncryptionKey' properties: { kty: 'RSA' @@ -65,7 +65,7 @@ resource geoBackupManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentit location: geoBackupLocation } -resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { +resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { name: geoBackupKeyVaultName location: geoBackupLocation properties: { @@ -74,8 +74,8 @@ resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId + softDeleteRetentionInDays: 90 // The resource provider requires a 90 day retention period for encryption. Anything less will cause the deployment to fail. Ref: https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-customer-managed-key#requirements-for-configuring-data-encryption-for-azure-database-for-mysql-flexible-server enablePurgeProtection: true // Required for encryption to work - softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true @@ -83,7 +83,7 @@ resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { accessPolicies: [] } - resource key 'keys@2023-02-01' = { + resource key 'keys@2023-07-01' = { name: 'keyEncryptionKey' properties: { kty: 'RSA' diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/main.test.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/main.test.bicep index 09db4d5368..0255def79e 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/main.test.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/main.test.bicep @@ -11,9 +11,6 @@ metadata description = 'This instance deploys the module with most of its featur @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dbformysql.flexibleservers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'dfmsmax' @@ -27,6 +24,10 @@ param baseTime string = utcNow('u') @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// Pipeline is selecting random regions which dont support all cosmos features and have constraints when creating new cosmos +#disable-next-line no-hardcoded-location +var enforcedLocation = 'northeurope' + // ============ // // Dependencies // // ============ // @@ -35,15 +36,15 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies1 'dependencies1.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies1' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies1' params: { // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) - location: resourceLocation + location: enforcedLocation managedIdentityName: 'dep-${namePrefix}-msi-ds-${serviceShort}' pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' } @@ -51,12 +52,12 @@ module nestedDependencies1 'dependencies1.bicep' = { module nestedDependencies2 'dependencies2.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies2' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies2' params: { // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - location: resourceLocation + location: enforcedLocation geoBackupKeyVaultName: 'dep-${namePrefix}-kvp-${serviceShort}-${substring(uniqueString(baseTime), 0, 2)}' geoBackupManagedIdentityName: 'dep-${namePrefix}-msip-${serviceShort}' geoBackupLocation: nestedDependencies1.outputs.pairedRegionName @@ -67,13 +68,13 @@ module nestedDependencies2 'dependencies2.bicep' = { // =========== module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -85,21 +86,23 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation lock: { kind: 'CanNotDelete' name: 'myCustomLockName' } roleAssignments: [ { + name: '2478b63b-0cae-457f-9bd3-9feb00e1925b' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies1.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies1.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -154,7 +157,7 @@ module testDeployment '../../../main.bicep' = [ startIpAddress: '100.100.100.1' } ] - highAvailability: 'SameZone' + highAvailability: 'ZoneRedundant' storageAutoGrow: 'Enabled' version: '8.0.21' customerManagedKey: { diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/private/main.test.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/private/main.test.bicep index 4966686565..022d81ac27 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/private/main.test.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/private/main.test.bicep @@ -11,9 +11,6 @@ metadata description = 'This instance deploys the module with connectivity mode @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dbformysql.flexibleservers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'dfmspvt' @@ -24,6 +21,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// Pipeline is selecting random regions which dont support all cosmos features and have constraints when creating new cosmos +#disable-next-line no-hardcoded-location +var enforcedLocation = 'northeurope' + // ============ // // Dependencies // // ============ // @@ -32,16 +33,16 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -53,10 +54,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceGroup.location + location: enforcedLocation administratorLogin: 'adminUserName' administratorLoginPassword: password skuName: 'Standard_D2ds_v4' diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep index 9b2301311f..be7429e7f1 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep @@ -12,9 +12,6 @@ metadata description = 'This instance deploys the module in alignment with the b // e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-dbformysql.flexibleservers-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') // e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'dfmswaf' @@ -26,6 +23,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') param namePrefix string = '#_namePrefix_#' +// Pipeline is selecting random regions which dont support all cosmos features and have constraints when creating new cosmos +#disable-next-line no-hardcoded-location +var enforcedLocation = 'northeurope' + // ============ // // Dependencies // // ============ // @@ -34,7 +35,7 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -45,14 +46,18 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation + availabilityZone: '1' administratorLogin: 'adminUserName' administratorLoginPassword: password - skuName: 'Standard_B1ms' - tier: 'Burstable' + highAvailability: 'ZoneRedundant' + highAvailabilityZone: '2' + skuName: 'Standard_D2ds_v4' + tier: 'GeneralPurpose' + storageAutoGrow: 'Enabled' lock: { kind: 'CanNotDelete' name: 'myCustomLockName' diff --git a/avm/res/db-for-my-sql/flexible-server/version.json b/avm/res/db-for-my-sql/flexible-server/version.json index 7fa401bdf7..c177b1bb58 100644 --- a/avm/res/db-for-my-sql/flexible-server/version.json +++ b/avm/res/db-for-my-sql/flexible-server/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/db-for-postgre-sql/flexible-server/README.md b/avm/res/db-for-postgre-sql/flexible-server/README.md index 6986b8fee6..236390be6e 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/README.md +++ b/avm/res/db-for-postgre-sql/flexible-server/README.md @@ -53,12 +53,18 @@ module flexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:' + administrators: [ + { + objectId: '' + principalName: '' + principalType: 'ServicePrincipal' + } + ] geoRedundantBackup: 'Enabled' + highAvailability: 'ZoneRedundant' location: '' } } @@ -81,21 +87,27 @@ module flexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:" + "administrators": { + "value": [ + { + "objectId": "", + "principalName": "", + "principalType": "ServicePrincipal" + } + ] }, "geoRedundantBackup": { "value": "Enabled" }, + "highAvailability": { + "value": "ZoneRedundant" + }, "location": { "value": "" } @@ -697,7 +709,14 @@ module flexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:' + maintenanceWindow: { + customWindow: 'Enabled' + dayOfWeek: 0 + startHour: 1 + startMinute: 0 + } privateDnsZoneArmResourceId: '' tags: { Environment: 'Non-Prod' @@ -782,9 +801,20 @@ module flexibleServer 'br/public:avm/res/db-for-postgre-sql/flexible-server:" }, + "maintenanceWindow": { + "value": { + "customWindow": "Enabled", + "dayOfWeek": 0, + "startHour": 1, + "startMinute": 0 + } + }, "privateDnsZoneArmResourceId": { "value": "" }, @@ -1254,7 +1284,7 @@ The mode for high availability. - Required: No - Type: string -- Default: `'Disabled'` +- Default: `'ZoneRedundant'` - Allowed: ```Bicep [ @@ -1314,7 +1344,15 @@ Properties for the maintenence window. If provided, 'customWindow' property must - Required: No - Type: object -- Default: `{}` +- Default: + ```Bicep + { + customWindow: 'Enabled' + dayOfWeek: 0 + startHour: 1 + startMinute: 0 + } + ``` ### Parameter: `passwordAuth` @@ -1361,6 +1399,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1411,6 +1450,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1472,7 +1518,7 @@ PostgreSQL Server version. - Required: No - Type: string -- Default: `'15'` +- Default: `'16'` - Allowed: ```Bicep [ @@ -1481,6 +1527,7 @@ PostgreSQL Server version. '13' '14' '15' + '16' ] ``` diff --git a/avm/res/db-for-postgre-sql/flexible-server/administrator/main.json b/avm/res/db-for-postgre-sql/flexible-server/administrator/main.json index 69197e9646..b7337b4766 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/administrator/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/administrator/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "17302939650459446486" + "version": "0.29.47.4906", + "templateHash": "9786947819042824705" }, "name": "DBforPostgreSQL Flexible Server Administrators", "description": "This module deploys a DBforPostgreSQL Flexible Server Administrator.", diff --git a/avm/res/db-for-postgre-sql/flexible-server/configuration/main.json b/avm/res/db-for-postgre-sql/flexible-server/configuration/main.json index 82ff31c783..8903e9ef21 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/configuration/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/configuration/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "4808844582468160626" + "version": "0.29.47.4906", + "templateHash": "3802666632340288344" }, "name": "DBforPostgreSQL Flexible Server Configurations", "description": "This module deploys a DBforPostgreSQL Flexible Server Configuration.", diff --git a/avm/res/db-for-postgre-sql/flexible-server/database/main.json b/avm/res/db-for-postgre-sql/flexible-server/database/main.json index a628aabe2a..1b15b5c7a4 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/database/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/database/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12256776332622303948" + "version": "0.29.47.4906", + "templateHash": "17501165975344742322" }, "name": "DBforPostgreSQL Flexible Server Databases", "description": "This module deploys a DBforPostgreSQL Flexible Server Database.", diff --git a/avm/res/db-for-postgre-sql/flexible-server/firewall-rule/main.json b/avm/res/db-for-postgre-sql/flexible-server/firewall-rule/main.json index b7c6ec5209..572f886bfe 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/firewall-rule/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/firewall-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "12309357869547394167" + "version": "0.29.47.4906", + "templateHash": "5110779562094536429" }, "name": "DBforPostgreSQL Flexible Server Firewall Rules", "description": "This module deploys a DBforPostgreSQL Flexible Server Firewall Rule.", diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.bicep b/avm/res/db-for-postgre-sql/flexible-server/main.bicep index 58f76dcdfe..24a84a31e8 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/main.bicep @@ -89,9 +89,10 @@ param storageSizeGB int = 32 '13' '14' '15' + '16' ]) @description('Optional. PostgreSQL Server version.') -param version string = '15' +param version string = '16' @allowed([ 'Disabled' @@ -99,7 +100,7 @@ param version string = '15' 'ZoneRedundant' ]) @description('Optional. The mode for high availability.') -param highAvailability string = 'Disabled' +param highAvailability string = 'ZoneRedundant' @allowed([ 'Create' @@ -117,7 +118,12 @@ param managedIdentities managedIdentitiesType param customerManagedKey customerManagedKeyType @description('Optional. Properties for the maintenence window. If provided, \'customWindow\' property must exist and set to \'Enabled\'.') -param maintenanceWindow object = {} +param maintenanceWindow object = { + customWindow: 'Enabled' + dayOfWeek: 0 + startHour: 1 + startMinute: 0 +} @description('Conditional. Required if \'createMode\' is set to \'PointInTimeRestore\'.') param pointInTimeUTC string = '' @@ -182,6 +188,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.dbforpostgresql-flexibleserver.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -292,14 +309,10 @@ resource flexibleServer_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource flexibleServer_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(flexibleServer.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(flexibleServer.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -317,8 +330,8 @@ module flexibleServer_databases 'database/main.bicep' = [ params: { name: database.name flexibleServerName: flexibleServer.name - collation: contains(database, 'collation') ? database.collation : '' - charset: contains(database, 'charset') ? database.charset : '' + collation: database.?collation ?? '' + charset: database.?charset ?? '' } } ] @@ -345,8 +358,8 @@ module flexibleServer_configurations 'configuration/main.bicep' = [ params: { name: configuration.name flexibleServerName: flexibleServer.name - source: contains(configuration, 'source') ? configuration.source : '' - value: contains(configuration, 'value') ? configuration.value : '' + source: configuration.?source ?? '' + value: configuration.?value ?? '' } dependsOn: [ flexibleServer_firewallRules @@ -362,7 +375,7 @@ module flexibleServer_administrators 'administrator/main.bicep' = [ objectId: administrator.objectId principalName: administrator.principalName principalType: administrator.principalType - tenantId: contains(administrator, 'tenantId') ? administrator.tenantId : tenant().tenantId + tenantId: administrator.?tenantId ?? tenant().tenantId } } ] @@ -429,6 +442,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.json b/avm/res/db-for-postgre-sql/flexible-server/main.json index f89dcb945d..2c67a4f617 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6917599378543885817" + "version": "0.29.47.4906", + "templateHash": "4308585226053074434" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server.", @@ -58,6 +58,13 @@ "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": { @@ -406,13 +413,14 @@ }, "version": { "type": "string", - "defaultValue": "15", + "defaultValue": "16", "allowedValues": [ "11", "12", "13", "14", - "15" + "15", + "16" ], "metadata": { "description": "Optional. PostgreSQL Server version." @@ -420,7 +428,7 @@ }, "highAvailability": { "type": "string", - "defaultValue": "Disabled", + "defaultValue": "ZoneRedundant", "allowedValues": [ "Disabled", "SameZone", @@ -457,7 +465,12 @@ }, "maintenanceWindow": { "type": "object", - "defaultValue": {}, + "defaultValue": { + "customWindow": "Enabled", + "dayOfWeek": 0, + "startHour": 1, + "startMinute": 0 + }, "metadata": { "description": "Optional. Properties for the maintenence window. If provided, 'customWindow' property must exist and set to 'Enabled'." } @@ -545,6 +558,13 @@ } }, "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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -667,20 +687,20 @@ "flexibleServer_roleAssignments": { "copy": { "name": "flexibleServer_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DBforPostgreSQL/flexibleServers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "flexibleServer" @@ -747,8 +767,12 @@ "flexibleServerName": { "value": "[parameters('name')]" }, - "collation": "[if(contains(parameters('databases')[copyIndex()], 'collation'), createObject('value', parameters('databases')[copyIndex()].collation), createObject('value', ''))]", - "charset": "[if(contains(parameters('databases')[copyIndex()], 'charset'), createObject('value', parameters('databases')[copyIndex()].charset), createObject('value', ''))]" + "collation": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'collation'), '')]" + }, + "charset": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'charset'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -756,8 +780,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6060199938382421423" + "version": "0.29.47.4906", + "templateHash": "17501165975344742322" }, "name": "DBforPostgreSQL Flexible Server Databases", "description": "This module deploys a DBforPostgreSQL Flexible Server Database.", @@ -864,8 +888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18047073594923038356" + "version": "0.29.47.4906", + "templateHash": "5110779562094536429" }, "name": "DBforPostgreSQL Flexible Server Firewall Rules", "description": "This module deploys a DBforPostgreSQL Flexible Server Firewall Rule.", @@ -960,8 +984,12 @@ "flexibleServerName": { "value": "[parameters('name')]" }, - "source": "[if(contains(parameters('configurations')[copyIndex()], 'source'), createObject('value', parameters('configurations')[copyIndex()].source), createObject('value', ''))]", - "value": "[if(contains(parameters('configurations')[copyIndex()], 'value'), createObject('value', parameters('configurations')[copyIndex()].value), createObject('value', ''))]" + "source": { + "value": "[coalesce(tryGet(parameters('configurations')[copyIndex()], 'source'), '')]" + }, + "value": { + "value": "[coalesce(tryGet(parameters('configurations')[copyIndex()], 'value'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -969,8 +997,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1739518705583408324" + "version": "0.29.47.4906", + "templateHash": "3802666632340288344" }, "name": "DBforPostgreSQL Flexible Server Configurations", "description": "This module deploys a DBforPostgreSQL Flexible Server Configuration.", @@ -1071,7 +1099,9 @@ "principalType": { "value": "[parameters('administrators')[copyIndex()].principalType]" }, - "tenantId": "[if(contains(parameters('administrators')[copyIndex()], 'tenantId'), createObject('value', parameters('administrators')[copyIndex()].tenantId), createObject('value', tenant().tenantId))]" + "tenantId": { + "value": "[coalesce(tryGet(parameters('administrators')[copyIndex()], 'tenantId'), tenant().tenantId)]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1079,8 +1109,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18164580138640455419" + "version": "0.29.47.4906", + "templateHash": "9786947819042824705" }, "name": "DBforPostgreSQL Flexible Server Administrators", "description": "This module deploys a DBforPostgreSQL Flexible Server Administrator.", diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/dependencies.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..7ddc0bfb00 --- /dev/null +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +@description('The client ID of the created Managed Identity.') +output managedIdentityClientId string = managedIdentity.properties.clientId + +@description('The name of the created Managed Identity.') +output managedIdentityName string = managedIdentity.name diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/main.test.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/main.test.bicep index f7848f4b41..6c56f5b441 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/main.test.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/defaults/main.test.bicep @@ -17,10 +17,6 @@ param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'dfpsfsmin' -@description('Optional. The password to leverage for the login.') -@secure() -param password string = newGuid() - @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -35,21 +31,37 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { location: resourceLocation } +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} // ============== // // Test Execution // // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - administratorLogin: 'adminUserName' - administratorLoginPassword: password - skuName: 'Standard_B2s' - tier: 'Burstable' - geoRedundantBackup: 'Enabled' +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + administrators: [ + { + objectId: nestedDependencies.outputs.managedIdentityClientId + principalName: nestedDependencies.outputs.managedIdentityName + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard_D2s_v3' + tier: 'GeneralPurpose' + geoRedundantBackup: 'Enabled' + highAvailability: 'ZoneRedundant' + } } -}] +] diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/public/main.test.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/public/main.test.bicep index 834c02cbb9..01b6ee55cc 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/public/main.test.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/public/main.test.bicep @@ -59,99 +59,100 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - administrators: [ - { - objectId: nestedDependencies.outputs.managedIdentityClientId - principalName: nestedDependencies.outputs.managedIdentityName - principalType: 'ServicePrincipal' - } - ] - skuName: 'Standard_D2s_v3' - tier: 'GeneralPurpose' - backupRetentionDays: 20 - configurations: [ - { - name: 'log_min_messages' - source: 'user-override' - value: 'INFO' - } - ] - databases: [ - { - charset: 'UTF8' - collation: 'en_US.utf8' - name: 'testdb1' - } - { - name: 'testdb2' - } - ] - roleAssignments: [ - { - roleDefinitionIdOrName: 'Owner' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } - { - roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + administrators: [ + { + objectId: nestedDependencies.outputs.managedIdentityClientId + principalName: nestedDependencies.outputs.managedIdentityName + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard_D2s_v3' + tier: 'GeneralPurpose' + backupRetentionDays: 20 + configurations: [ + { + name: 'log_min_messages' + source: 'user-override' + value: 'INFO' + } + ] + databases: [ + { + charset: 'UTF8' + collation: 'en_US.utf8' + name: 'testdb1' + } + { + name: 'testdb2' + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + firewallRules: [ + { + endIpAddress: '0.0.0.0' + name: 'AllowAllWindowsAzureIps' + startIpAddress: '0.0.0.0' + } + { + endIpAddress: '10.10.10.10' + name: 'test-rule1' + startIpAddress: '10.10.10.1' + } + { + endIpAddress: '100.100.100.10' + name: 'test-rule2' + startIpAddress: '100.100.100.1' + } + ] + geoRedundantBackup: 'Disabled' + highAvailability: 'SameZone' + location: resourceLocation + storageSizeGB: 1024 + version: '14' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' } - { - roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } - ] - diagnosticSettings: [ - { - name: 'customSetting' - metricCategories: [ - { - category: 'AllMetrics' - } - ] - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId - } - ] - firewallRules: [ - { - endIpAddress: '0.0.0.0' - name: 'AllowAllWindowsAzureIps' - startIpAddress: '0.0.0.0' - } - { - endIpAddress: '10.10.10.10' - name: 'test-rule1' - startIpAddress: '10.10.10.1' - } - { - endIpAddress: '100.100.100.10' - name: 'test-rule2' - startIpAddress: '100.100.100.1' - } - ] - geoRedundantBackup: 'Disabled' - highAvailability: 'SameZone' - location: resourceLocation - storageSizeGB: 1024 - version: '14' - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] -}] +] diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep index 395890f8fb..f33e849678 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/waf-aligned/main.test.bicep @@ -62,62 +62,71 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - administrators: [ - { - objectId: nestedDependencies.outputs.managedIdentityClientId - principalName: nestedDependencies.outputs.managedIdentityName - principalType: 'ServicePrincipal' - } - ] - skuName: 'Standard_D2s_v3' - tier: 'GeneralPurpose' - configurations: [ - { - name: 'log_min_messages' - source: 'user-override' - value: 'INFO' - } - { - name: 'autovacuum_naptime' - source: 'user-override' - value: '80' +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + administrators: [ + { + objectId: nestedDependencies.outputs.managedIdentityClientId + principalName: nestedDependencies.outputs.managedIdentityName + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard_D2s_v3' + tier: 'GeneralPurpose' + configurations: [ + { + name: 'log_min_messages' + source: 'user-override' + value: 'INFO' + } + { + name: 'autovacuum_naptime' + source: 'user-override' + value: '80' + } + ] + databases: [ + { + charset: 'UTF8' + collation: 'en_US.utf8' + name: 'testdb1' + } + { + name: 'testdb2' + } + ] + delegatedSubnetResourceId: nestedDependencies.outputs.subnetResourceId + diagnosticSettings: [ + { + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + geoRedundantBackup: 'Enabled' + privateDnsZoneArmResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' } - ] - databases: [ - { - charset: 'UTF8' - collation: 'en_US.utf8' - name: 'testdb1' - } - { - name: 'testdb2' - } - ] - delegatedSubnetResourceId: nestedDependencies.outputs.subnetResourceId - diagnosticSettings: [ - { - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + maintenanceWindow: { + customWindow: 'Enabled' + dayOfWeek: 0 + startHour: 1 + startMinute: 0 } - ] - geoRedundantBackup: 'Enabled' - privateDnsZoneArmResourceId: nestedDependencies.outputs.privateDNSZoneResourceId - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' + highAvailability: 'ZoneRedundant' } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] -}] +] diff --git a/avm/res/db-for-postgre-sql/flexible-server/version.json b/avm/res/db-for-postgre-sql/flexible-server/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/version.json +++ b/avm/res/db-for-postgre-sql/flexible-server/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/desktop-virtualization/application-group/README.md b/avm/res/desktop-virtualization/application-group/README.md index d29b429e5d..8d810ed523 100644 --- a/avm/res/desktop-virtualization/application-group/README.md +++ b/avm/res/desktop-virtualization/application-group/README.md @@ -147,11 +147,13 @@ module applicationGroup 'br/public:avm/res/desktop-virtualization/application-gr } roleAssignments: [ { + name: '30eaf006-ee2d-4a95-921c-87dfdb4c2061' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -245,11 +247,13 @@ module applicationGroup 'br/public:avm/res/desktop-virtualization/application-gr "roleAssignments": { "value": [ { + "name": "30eaf006-ee2d-4a95-921c-87dfdb4c2061", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -622,6 +626,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -672,6 +677,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/desktop-virtualization/application-group/application/main.json b/avm/res/desktop-virtualization/application-group/application/main.json index c2a2d880fb..32d61cf76b 100644 --- a/avm/res/desktop-virtualization/application-group/application/main.json +++ b/avm/res/desktop-virtualization/application-group/application/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11159515031632739969" + "version": "0.29.47.4906", + "templateHash": "10838639279177354544" }, "name": "Azure Virtual Desktop Application Group Application", "description": "This module deploys an Azure Virtual Desktop Application Group Application.", diff --git a/avm/res/desktop-virtualization/application-group/main.bicep b/avm/res/desktop-virtualization/application-group/main.bicep index 9e72fe85c0..7a54d23c4f 100644 --- a/avm/res/desktop-virtualization/application-group/main.bicep +++ b/avm/res/desktop-virtualization/application-group/main.bicep @@ -68,6 +68,17 @@ var builtInRoleNames = { 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.desktopvirtualization-appgroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -133,19 +144,15 @@ resource appGroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(l } resource appGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(appGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(appGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType condition: roleAssignment.?condition - conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId } scope: appGroup @@ -199,6 +206,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/desktop-virtualization/application-group/main.json b/avm/res/desktop-virtualization/application-group/main.json index 38d7efa319..be13b3e18a 100644 --- a/avm/res/desktop-virtualization/application-group/main.json +++ b/avm/res/desktop-virtualization/application-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7666140066506377288" + "version": "0.29.47.4906", + "templateHash": "10367024700295451803" }, "name": "Azure Virtual Desktop Application Group", "description": "This module deploys an Azure Virtual Desktop Application Group.", @@ -43,6 +43,13 @@ "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": { @@ -286,6 +293,13 @@ } }, "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": { "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", @@ -371,20 +385,20 @@ "appGroup_roleAssignments": { "copy": { "name": "appGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DesktopVirtualization/applicationGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "appGroup" @@ -459,8 +473,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13159129056123729816" + "version": "0.29.47.4906", + "templateHash": "10838639279177354544" }, "name": "Azure Virtual Desktop Application Group Application", "description": "This module deploys an Azure Virtual Desktop Application Group Application.", diff --git a/avm/res/desktop-virtualization/application-group/tests/e2e/max/main.test.bicep b/avm/res/desktop-virtualization/application-group/tests/e2e/max/main.test.bicep index 6085203f7a..22d1751b1d 100644 --- a/avm/res/desktop-virtualization/application-group/tests/e2e/max/main.test.bicep +++ b/avm/res/desktop-virtualization/application-group/tests/e2e/max/main.test.bicep @@ -106,11 +106,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '30eaf006-ee2d-4a95-921c-87dfdb4c2061' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/desktop-virtualization/application-group/version.json b/avm/res/desktop-virtualization/application-group/version.json index 7245f14872..729ac87673 100644 --- a/avm/res/desktop-virtualization/application-group/version.json +++ b/avm/res/desktop-virtualization/application-group/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", - "pathFilters": [ - "./main.json" - ] - } \ No newline at end of file + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.2", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file diff --git a/avm/res/desktop-virtualization/scaling-plan/README.md b/avm/res/desktop-virtualization/scaling-plan/README.md index 96e75b6ab4..b599567220 100644 --- a/avm/res/desktop-virtualization/scaling-plan/README.md +++ b/avm/res/desktop-virtualization/scaling-plan/README.md @@ -125,11 +125,13 @@ module scalingPlan 'br/public:avm/res/desktop-virtualization/scaling-plan:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -316,11 +318,13 @@ module scalingPlan 'br/public:avm/res/desktop-virtualization/scaling-plan:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -809,6 +813,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -859,6 +864,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/desktop-virtualization/scaling-plan/main.bicep b/avm/res/desktop-virtualization/scaling-plan/main.bicep index ee96726732..15ad9a9d17 100644 --- a/avm/res/desktop-virtualization/scaling-plan/main.bicep +++ b/avm/res/desktop-virtualization/scaling-plan/main.bicep @@ -70,6 +70,17 @@ var builtInRoleNames = { 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.desktopvirtualization-scalingplan.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -116,14 +127,10 @@ resource scalingPlan_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empt } resource scalingPlan_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(scalingPlan.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(scalingPlan.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -182,6 +189,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/desktop-virtualization/scaling-plan/main.json b/avm/res/desktop-virtualization/scaling-plan/main.json index 074629abf7..1c4786cdbe 100644 --- a/avm/res/desktop-virtualization/scaling-plan/main.json +++ b/avm/res/desktop-virtualization/scaling-plan/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2173334947824258553" + "version": "0.29.47.4906", + "templateHash": "7189430344651214468" }, "name": "Azure Virtual Desktop Scaling Plan", "description": "This module deploys an Azure Virtual Desktop Scaling Plan.", @@ -43,6 +43,13 @@ "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": { @@ -301,6 +308,13 @@ } }, "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": { "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", @@ -377,20 +391,20 @@ "scalingPlan_roleAssignments": { "copy": { "name": "scalingPlan_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DesktopVirtualization/scalingPlans/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "scalingPlan" diff --git a/avm/res/desktop-virtualization/scaling-plan/tests/e2e/max/main.test.bicep b/avm/res/desktop-virtualization/scaling-plan/tests/e2e/max/main.test.bicep index a39813e0ff..2091b07705 100644 --- a/avm/res/desktop-virtualization/scaling-plan/tests/e2e/max/main.test.bicep +++ b/avm/res/desktop-virtualization/scaling-plan/tests/e2e/max/main.test.bicep @@ -200,11 +200,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'c2c1c560-2169-405a-a8dc-7427e403e5ac' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/desktop-virtualization/scaling-plan/version.json b/avm/res/desktop-virtualization/scaling-plan/version.json index 7245f14872..729ac87673 100644 --- a/avm/res/desktop-virtualization/scaling-plan/version.json +++ b/avm/res/desktop-virtualization/scaling-plan/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", - "pathFilters": [ - "./main.json" - ] - } \ No newline at end of file + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.2", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file diff --git a/avm/res/desktop-virtualization/workspace/README.md b/avm/res/desktop-virtualization/workspace/README.md index 9c2402e992..c1b73e2b05 100644 --- a/avm/res/desktop-virtualization/workspace/README.md +++ b/avm/res/desktop-virtualization/workspace/README.md @@ -19,8 +19,8 @@ This module deploys an Azure Virtual Desktop Workspace. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.DesktopVirtualization/workspaces` | [2022-10-14-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DesktopVirtualization/2022-10-14-preview/workspaces) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -140,9 +140,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { principalId: '' @@ -170,9 +174,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { principalId: '' @@ -192,11 +200,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: 'e31e3fcd-816f-49b9-a741-feff792a56d7' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -286,9 +296,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { "principalId": "", @@ -316,9 +330,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { "principalId": "", @@ -342,11 +360,13 @@ module workspace 'br/public:avm/res/desktop-virtualization/workspace:' "roleAssignments": { "value": [ { + "name": "e31e3fcd-816f-49b9-a741-feff792a56d7", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -673,7 +693,6 @@ Configuration details for private endpoints. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "feed" or "global". | | [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | **Optional parameters** @@ -690,19 +709,13 @@ Configuration details for private endpoints. | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | -### Parameter: `privateEndpoints.service` - -The service (sub-) type to deploy the private endpoint for. For example "feed" or "global". - -- Required: Yes -- Type: string - ### Parameter: `privateEndpoints.subnetResourceId` Resource ID of the subnet where the endpoint needs to be created. @@ -880,19 +893,71 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the Private DNS Zone Group. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private link connection to create. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.resourceGroupName` @@ -923,6 +988,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -973,6 +1039,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -990,6 +1063,13 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `privateEndpoints.service` + +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.tags` Tags to be applied on all resources/resource groups in this deployment. @@ -1027,6 +1107,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1077,6 +1158,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1108,6 +1196,7 @@ Tags of the resource. | :-- | :-- | :-- | | `location` | string | The location of the workspace. | | `name` | string | The name of the workspace. | +| `privateEndpoints` | array | The private endpoints of the workspace. | | `resourceGroupName` | string | The name of the resource group the workspace was created in. | | `resourceId` | string | The resource ID of the workspace. | @@ -1117,7 +1206,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/desktop-virtualization/workspace/main.bicep b/avm/res/desktop-virtualization/workspace/main.bicep index 93b849f3fd..c9a61e0606 100644 --- a/avm/res/desktop-virtualization/workspace/main.bicep +++ b/avm/res/desktop-virtualization/workspace/main.bicep @@ -60,6 +60,17 @@ var builtInRoleNames = { 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -94,7 +105,7 @@ resource workspace 'Microsoft.DesktopVirtualization/workspaces@2022-10-14-previe } } -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -135,8 +146,7 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -159,19 +169,15 @@ resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty( } resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType condition: roleAssignment.?condition - conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId } scope: workspace @@ -211,6 +217,17 @@ output name string = workspace.name @sys.description('The location of the workspace.') output location string = workspace.location +@sys.description('The private endpoints of the workspace.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: workspace_privateEndpoints[i].outputs.name + resourceId: workspace_privateEndpoints[i].outputs.resourceId + groupId: workspace_privateEndpoints[i].outputs.groupId + customDnsConfig: workspace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: workspace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // ================ // // Definitions // // ================ // @@ -248,6 +265,9 @@ type diagnosticSettingType = { }[]? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -277,17 +297,29 @@ type privateEndpointType = { @sys.description('Optional. The location to deploy the private endpoint to.') location: string? - @sys.description('Required. The service (sub-) type to deploy the private endpoint for. For example "feed" or "global".') - service: string + @sys.description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + + @sys.description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') + service: string? @sys.description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @sys.description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @sys.description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @sys.description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @sys.description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @sys.description('Optional. The name of the private DNS zone group config.') + name: string? - @sys.description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @sys.description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @sys.description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/desktop-virtualization/workspace/main.json b/avm/res/desktop-virtualization/workspace/main.json index 2422c63474..2197a9e47d 100644 --- a/avm/res/desktop-virtualization/workspace/main.json +++ b/avm/res/desktop-virtualization/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14674962992794168390" + "version": "0.29.47.4906", + "templateHash": "11955333663184488007" }, "name": "Workspace", "description": "This module deploys an Azure Virtual Desktop Workspace.", @@ -106,6 +106,13 @@ "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": { @@ -186,33 +193,64 @@ "description": "Optional. The location to deploy the private endpoint to." } }, - "service": { + "privateLinkServiceConnectionName": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"feed\" or \"global\"." + "description": "Optional. The name of the private link connection to create." } }, - "subnetResourceId": { + "service": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." } }, - "privateDnsZoneGroupName": { + "subnetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -464,6 +502,13 @@ } }, "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": { "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", @@ -537,20 +582,20 @@ "workspace_roleAssignments": { "copy": { "name": "workspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DesktopVirtualization/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DesktopVirtualization/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DesktopVirtualization/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "workspace" @@ -610,11 +655,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -642,19 +684,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -758,13 +828,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -798,8 +868,11 @@ "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." + "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": { @@ -839,8 +912,11 @@ "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." + "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": { @@ -888,6 +964,29 @@ } }, "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": { @@ -923,18 +1022,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -990,6 +1082,13 @@ } }, "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')]", @@ -1007,8 +1106,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1026,7 +1125,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1067,27 +1166,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1098,28 +1197,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1127,12 +1250,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1146,27 +1272,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1224,14 +1359,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1269,6 +1418,22 @@ "description": "The location of the workspace." }, "value": "[reference('workspace', '2022-10-14-preview', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the workspace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('workspace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/desktop-virtualization/workspace/tests/e2e/max/main.test.bicep b/avm/res/desktop-virtualization/workspace/tests/e2e/max/main.test.bicep index 4ba1325da5..ab63e7aa85 100644 --- a/avm/res/desktop-virtualization/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/desktop-virtualization/workspace/tests/e2e/max/main.test.bicep @@ -84,11 +84,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'e31e3fcd-816f-49b9-a741-feff792a56d7' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -105,9 +107,13 @@ module testDeployment '../../../main.bicep' = [ privateEndpoints: [ { service: 'feed' - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -143,9 +149,13 @@ module testDeployment '../../../main.bicep' = [ } { service: 'global' - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' diff --git a/avm/res/desktop-virtualization/workspace/version.json b/avm/res/desktop-virtualization/workspace/version.json index 13669e6601..21226dd43f 100644 --- a/avm/res/desktop-virtualization/workspace/version.json +++ b/avm/res/desktop-virtualization/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/dev-test-lab/lab/README.md b/avm/res/dev-test-lab/lab/README.md index 462e6836ac..5fd0c93362 100644 --- a/avm/res/dev-test-lab/lab/README.md +++ b/avm/res/dev-test-lab/lab/README.md @@ -109,12 +109,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { } artifactsources: [ { - branchRef: 'master' displayName: 'Public Artifact Repo' folderPath: '/Artifacts' name: 'Public Repo' sourceType: 'GitHub' - status: 'Disabled' + status: 'Enabled' uri: 'https://github.com/Azure/azure-devtestlab.git' } { @@ -124,12 +123,28 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { name: 'Public Environment Repo' sourceType: 'GitHub' status: 'Disabled' + tags: { + 'hidden-title': 'This is visible in the resource name' + labName: 'dtllmax001' + resourceType: 'DevTest Lab' + } + uri: 'https://github.com/Azure/azure-devtestlab.git' + } + { + armTemplateFolderPath: '/ArmTemplates' + branchRef: 'main' + displayName: 'Private Artifact Repo' + folderPath: '/Artifacts' + name: 'Private Repo' + securityToken: '' + status: 'Disabled' uri: 'https://github.com/Azure/azure-devtestlab.git' } ] artifactsStorageAccount: '' browserConnect: 'Enabled' costs: { + currencyCode: 'AUD' cycleType: 'CalendarMonth' status: 'Enabled' target: 450 @@ -163,9 +178,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { description: 'Integration configured for auto-shutdown' emailRecipient: 'mail@contosodtlmail.com' events: [ - { - eventName: 'AutoShutdown' - } + 'AutoShutdown' ] name: 'autoShutdown' notificationLocale: 'en' @@ -173,9 +186,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { } { events: [ - { - eventName: 'Cost' - } + 'Cost' ] name: 'costThreshold' webHookUrl: 'https://webhook.contosotest.com' @@ -245,11 +256,13 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { premiumDataDisks: 'Enabled' roleAssignments: [ { + name: 'b08c589c-2c79-41bd-8195-d5e62ad12f67' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -266,8 +279,10 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { time: '0000' } name: 'LabVmsShutdown' - notificationSettingsStatus: 'Enabled' - notificationSettingsTimeInMinutes: 30 + notificationSettings: { + status: 'Enabled' + timeInMinutes: 30 + } status: 'Enabled' taskType: 'LabVmsShutdownTask' timeZoneId: 'AUS Eastern Standard Time' @@ -365,12 +380,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "artifactsources": { "value": [ { - "branchRef": "master", "displayName": "Public Artifact Repo", "folderPath": "/Artifacts", "name": "Public Repo", "sourceType": "GitHub", - "status": "Disabled", + "status": "Enabled", "uri": "https://github.com/Azure/azure-devtestlab.git" }, { @@ -380,6 +394,21 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "name": "Public Environment Repo", "sourceType": "GitHub", "status": "Disabled", + "tags": { + "hidden-title": "This is visible in the resource name", + "labName": "dtllmax001", + "resourceType": "DevTest Lab" + }, + "uri": "https://github.com/Azure/azure-devtestlab.git" + }, + { + "armTemplateFolderPath": "/ArmTemplates", + "branchRef": "main", + "displayName": "Private Artifact Repo", + "folderPath": "/Artifacts", + "name": "Private Repo", + "securityToken": "", + "status": "Disabled", "uri": "https://github.com/Azure/azure-devtestlab.git" } ] @@ -392,6 +421,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { }, "costs": { "value": { + "currencyCode": "AUD", "cycleType": "CalendarMonth", "status": "Enabled", "target": 450, @@ -449,9 +479,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "description": "Integration configured for auto-shutdown", "emailRecipient": "mail@contosodtlmail.com", "events": [ - { - "eventName": "AutoShutdown" - } + "AutoShutdown" ], "name": "autoShutdown", "notificationLocale": "en", @@ -459,9 +487,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { }, { "events": [ - { - "eventName": "Cost" - } + "Cost" ], "name": "costThreshold", "webHookUrl": "https://webhook.contosotest.com" @@ -537,11 +563,13 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "roleAssignments": { "value": [ { + "name": "b08c589c-2c79-41bd-8195-d5e62ad12f67", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -560,8 +588,10 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "time": "0000" }, "name": "LabVmsShutdown", - "notificationSettingsStatus": "Enabled", - "notificationSettingsTimeInMinutes": 30, + "notificationSettings": { + "status": "Enabled", + "timeInMinutes": 30 + }, "status": "Enabled", "taskType": "LabVmsShutdownTask", "timeZoneId": "AUS Eastern Standard Time" @@ -737,7 +767,7 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { | [`labStorageType`](#parameter-labstoragetype) | string | Type of storage used by the lab. It can be either Premium or Standard. | | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | -| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab. | | [`managementIdentitiesResourceIds`](#parameter-managementidentitiesresourceids) | array | The resource ID(s) to assign to the virtual machines associated with this lab. | | [`mandatoryArtifactsResourceIdsLinux`](#parameter-mandatoryartifactsresourceidslinux) | array | The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. | | [`mandatoryArtifactsResourceIdsWindows`](#parameter-mandatoryartifactsresourceidswindows) | array | The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. | @@ -771,7 +801,84 @@ Notification Channels to create for the lab. Required if the schedules property - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`events`](#parameter-notificationchannelsevents) | array | The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost". | +| [`name`](#parameter-notificationchannelsname) | string | The name of the notification channel. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-notificationchannelsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-notificationchannelswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-notificationchannelsdescription) | string | The description of the notification. | +| [`notificationLocale`](#parameter-notificationchannelsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`tags`](#parameter-notificationchannelstags) | object | The tags of the notification channel. | + +### Parameter: `notificationchannels.events` + +The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost". + +- Required: Yes +- Type: array + +### Parameter: `notificationchannels.name` + +The name of the notification channel. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'autoShutdown' + 'costThreshold' + ] + ``` + +### Parameter: `notificationchannels.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.webHookUrl` + +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.description` + +The description of the notification. + +- Required: No +- Type: string + +### Parameter: `notificationchannels.notificationLocale` + +The locale to use when sending a notification (fallback for unsupported languages is EN). + +- Required: No +- Type: string + +### Parameter: `notificationchannels.tags` + +The tags of the notification channel. + +- Required: No +- Type: object ### Parameter: `announcement` @@ -787,227 +894,248 @@ Artifact sources to create for the lab. - Required: No - Type: array -- Default: `[]` -### Parameter: `artifactsStorageAccount` +**Required parameters** -The resource ID of the storage account used to store artifacts and images by the lab. Also used for defaultStorageAccount, defaultPremiumStorageAccount and premiumDataDiskStorageAccount properties. If left empty, a default storage account will be created by the lab and used. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-artifactsourcesname) | string | The name of the artifact source. | +| [`uri`](#parameter-artifactsourcesuri) | string | The artifact source's URI. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`armTemplateFolderPath`](#parameter-artifactsourcesarmtemplatefolderpath) | string | The folder containing Azure Resource Manager templates. Required if "folderPath" is empty. | +| [`folderPath`](#parameter-artifactsourcesfolderpath) | string | The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`branchRef`](#parameter-artifactsourcesbranchref) | string | The artifact source's branch reference (e.g. main or master). | +| [`displayName`](#parameter-artifactsourcesdisplayname) | string | The display name of the artifact source. Default is the name of the artifact source. | +| [`securityToken`](#parameter-artifactsourcessecuritytoken) | securestring | The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity. | +| [`sourceType`](#parameter-artifactsourcessourcetype) | string | The artifact source's type. | +| [`status`](#parameter-artifactsourcesstatus) | string | Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled". | +| [`tags`](#parameter-artifactsourcestags) | object | The tags of the artifact source. | + +### Parameter: `artifactsources.name` + +The name of the artifact source. + +- Required: Yes +- Type: string + +### Parameter: `artifactsources.uri` + +The artifact source's URI. + +- Required: Yes +- Type: string + +### Parameter: `artifactsources.armTemplateFolderPath` + +The folder containing Azure Resource Manager templates. Required if "folderPath" is empty. - Required: No - Type: string -- Default: `''` -### Parameter: `browserConnect` +### Parameter: `artifactsources.folderPath` -Enable browser connect on virtual machines if the lab's VNETs have configured Azure Bastion. +The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty. - Required: No - Type: string -- Default: `'Disabled'` -- Allowed: - ```Bicep - [ - 'Disabled' - 'Enabled' - ] - ``` -### Parameter: `costs` +### Parameter: `artifactsources.branchRef` -Costs to create for the lab. +The artifact source's branch reference (e.g. main or master). - Required: No -- Type: object -- Default: `{}` +- Type: string -### Parameter: `disableAutoUpgradeCseMinorVersion` +### Parameter: `artifactsources.displayName` -Disable auto upgrade custom script extension minor version. +The display name of the artifact source. Default is the name of the artifact source. - Required: No -- Type: bool -- Default: `False` +- Type: string -### Parameter: `enableTelemetry` +### Parameter: `artifactsources.securityToken` -Enable/Disable usage telemetry for module. +The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity. - Required: No -- Type: bool -- Default: `True` +- Type: securestring -### Parameter: `encryptionType` +### Parameter: `artifactsources.sourceType` -Specify how OS and data disks created as part of the lab are encrypted. +The artifact source's type. - Required: No - Type: string -- Default: `'EncryptionAtRestWithPlatformKey'` - Allowed: ```Bicep [ - 'EncryptionAtRestWithCustomerKey' - 'EncryptionAtRestWithPlatformKey' + 'GitHub' + 'StorageAccount' + 'VsoGit' ] ``` -### Parameter: `environmentPermission` +### Parameter: `artifactsources.status` -The access rights to be granted to the user when provisioning an environment. +Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled". - Required: No - Type: string -- Default: `'Reader'` - Allowed: ```Bicep [ - 'Contributor' - 'Reader' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `extendedProperties` +### Parameter: `artifactsources.tags` -Extended properties of the lab used for experimental features. +The tags of the artifact source. - Required: No - Type: object -- Default: `{}` -### Parameter: `isolateLabResources` +### Parameter: `artifactsStorageAccount` -Enable lab resources isolation from the public internet. +The resource ID of the storage account used to store artifacts and images by the lab. Also used for defaultStorageAccount, defaultPremiumStorageAccount and premiumDataDiskStorageAccount properties. If left empty, a default storage account will be created by the lab and used. - Required: No - Type: string -- Default: `'Enabled'` -- Allowed: - ```Bicep - [ - 'Disabled' - 'Enabled' - ] - ``` +- Default: `''` -### Parameter: `labStorageType` +### Parameter: `browserConnect` -Type of storage used by the lab. It can be either Premium or Standard. +Enable browser connect on virtual machines if the lab's VNETs have configured Azure Bastion. - Required: No - Type: string -- Default: `'Premium'` +- Default: `'Disabled'` - Allowed: ```Bicep [ - 'Premium' - 'Standard' - 'StandardSSD' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `location` +### Parameter: `costs` -Location for all Resources. +Costs to create for the lab. - Required: No -- Type: string -- Default: `[resourceGroup().location]` - -### Parameter: `lock` +- Type: object -The lock settings of the service. +**Required parameters** -- Required: No -- Type: object +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cycleType`](#parameter-costscycletype) | string | Reporting cycle type. | -**Optional parameters** +**Conditional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | -| [`name`](#parameter-lockname) | string | Specify the name of lock. | +| [`cycleEndDateTime`](#parameter-costscycleenddatetime) | string | Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". | +| [`cycleStartDateTime`](#parameter-costscyclestartdatetime) | string | Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". | -### Parameter: `lock.kind` +**Optional parameters** -Specify the type of lock. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`currencyCode`](#parameter-costscurrencycode) | string | The currency code of the cost. Default is "USD". | +| [`status`](#parameter-costsstatus) | string | Target cost status. | +| [`tags`](#parameter-coststags) | object | The tags of the resource. | +| [`target`](#parameter-coststarget) | int | Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds. | +| [`thresholdValue100DisplayOnChart`](#parameter-coststhresholdvalue100displayonchart) | string | Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue100SendNotificationWhenExceeded`](#parameter-coststhresholdvalue100sendnotificationwhenexceeded) | string | Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue125DisplayOnChart`](#parameter-coststhresholdvalue125displayonchart) | string | Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue125SendNotificationWhenExceeded`](#parameter-coststhresholdvalue125sendnotificationwhenexceeded) | string | Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue25DisplayOnChart`](#parameter-coststhresholdvalue25displayonchart) | string | Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue25SendNotificationWhenExceeded`](#parameter-coststhresholdvalue25sendnotificationwhenexceeded) | string | Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue50DisplayOnChart`](#parameter-coststhresholdvalue50displayonchart) | string | Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue50SendNotificationWhenExceeded`](#parameter-coststhresholdvalue50sendnotificationwhenexceeded) | string | Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | +| [`thresholdValue75DisplayOnChart`](#parameter-coststhresholdvalue75displayonchart) | string | Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts. | +| [`thresholdValue75SendNotificationWhenExceeded`](#parameter-coststhresholdvalue75sendnotificationwhenexceeded) | string | Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. | + +### Parameter: `costs.cycleType` + +Reporting cycle type. -- Required: No +- Required: Yes - Type: string - Allowed: ```Bicep [ - 'CanNotDelete' - 'None' - 'ReadOnly' + 'CalendarMonth' + 'Custom' ] ``` -### Parameter: `lock.name` +### Parameter: `costs.cycleEndDateTime` -Specify the name of lock. +Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". - Required: No - Type: string -### Parameter: `managedIdentities` +### Parameter: `costs.cycleStartDateTime` -The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment. +Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom". - Required: No -- Type: object - -**Optional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. | - -### Parameter: `managedIdentities.userAssignedResourceIds` - -The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. - -- Required: Yes -- Type: array +- Type: string -### Parameter: `managementIdentitiesResourceIds` +### Parameter: `costs.currencyCode` -The resource ID(s) to assign to the virtual machines associated with this lab. +The currency code of the cost. Default is "USD". - Required: No -- Type: array -- Default: `[]` +- Type: string -### Parameter: `mandatoryArtifactsResourceIdsLinux` +### Parameter: `costs.status` -The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. +Target cost status. - Required: No -- Type: array -- Default: `[]` +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -### Parameter: `mandatoryArtifactsResourceIdsWindows` +### Parameter: `costs.tags` -The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. +The tags of the resource. - Required: No -- Type: array -- Default: `[]` +- Type: object -### Parameter: `policies` +### Parameter: `costs.target` -Policies to create for the lab. +Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds. - Required: No -- Type: array -- Default: `[]` +- Type: int -### Parameter: `premiumDataDisks` +### Parameter: `costs.thresholdValue100DisplayOnChart` -The setting to enable usage of premium data disks. When its value is "Enabled", creation of standard or premium data disks is allowed. When its value is "Disabled", only creation of standard data disks is allowed. Default is "Disabled". +Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts. - Required: No - Type: string -- Default: `'Disabled'` - Allowed: ```Bicep [ @@ -1016,9 +1144,308 @@ The setting to enable usage of premium data disks. When its value is "Enabled", ] ``` -### Parameter: `roleAssignments` +### Parameter: `costs.thresholdValue100SendNotificationWhenExceeded` -Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue125DisplayOnChart` + +Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue125SendNotificationWhenExceeded` + +Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue25DisplayOnChart` + +Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue25SendNotificationWhenExceeded` + +Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue50DisplayOnChart` + +Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue50SendNotificationWhenExceeded` + +Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue75DisplayOnChart` + +Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `costs.thresholdValue75SendNotificationWhenExceeded` + +Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `disableAutoUpgradeCseMinorVersion` + +Disable auto upgrade custom script extension minor version. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `encryptionType` + +Specify how OS and data disks created as part of the lab are encrypted. + +- Required: No +- Type: string +- Default: `'EncryptionAtRestWithPlatformKey'` +- Allowed: + ```Bicep + [ + 'EncryptionAtRestWithCustomerKey' + 'EncryptionAtRestWithPlatformKey' + ] + ``` + +### Parameter: `environmentPermission` + +The access rights to be granted to the user when provisioning an environment. + +- Required: No +- Type: string +- Default: `'Reader'` +- Allowed: + ```Bicep + [ + 'Contributor' + 'Reader' + ] + ``` + +### Parameter: `extendedProperties` + +Extended properties of the lab used for experimental features. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `isolateLabResources` + +Enable lab resources isolation from the public internet. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `labStorageType` + +Type of storage used by the lab. It can be either Premium or Standard. + +- Required: No +- Type: string +- Default: `'Premium'` +- Allowed: + ```Bicep + [ + 'Premium' + 'Standard' + 'StandardSSD' + ] + ``` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. | + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. Currently, a single user-assigned identity is supported per lab. + +- Required: Yes +- Type: array + +### Parameter: `managementIdentitiesResourceIds` + +The resource ID(s) to assign to the virtual machines associated with this lab. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `mandatoryArtifactsResourceIdsLinux` + +The ordered list of artifact resource IDs that should be applied on all Linux VM creations by default, prior to the artifacts specified by the user. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `mandatoryArtifactsResourceIdsWindows` + +The ordered list of artifact resource IDs that should be applied on all Windows VM creations by default, prior to the artifacts specified by the user. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `policies` + +Policies to create for the lab. - Required: No - Type: array @@ -1027,91 +1454,430 @@ Array of role assignment objects that contain the 'roleDefinitionIdOrName' and ' | Parameter | Type | Description | | :-- | :-- | :-- | -| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | -| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| [`evaluatorType`](#parameter-policiesevaluatortype) | string | The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy). | +| [`factName`](#parameter-policiesfactname) | string | The fact name of the policy. | +| [`name`](#parameter-policiesname) | string | The name of the policy. | +| [`threshold`](#parameter-policiesthreshold) | string | The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy). | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-policiesdescription) | string | The description of the policy. | +| [`factData`](#parameter-policiesfactdata) | string | The fact data of the policy. | +| [`status`](#parameter-policiesstatus) | string | The status of the policy. Default is "Enabled". | + +### Parameter: `policies.evaluatorType` + +The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy). + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AllowedValuesPolicy' + 'MaxValuePolicy' + ] + ``` + +### Parameter: `policies.factName` + +The fact name of the policy. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'EnvironmentTemplate' + 'GalleryImage' + 'LabPremiumVmCount' + 'LabTargetCost' + 'LabVmCount' + 'LabVmSize' + 'ScheduleEditPermission' + 'UserOwnedLabPremiumVmCount' + 'UserOwnedLabVmCount' + 'UserOwnedLabVmCountInSubnet' + ] + ``` + +### Parameter: `policies.name` + +The name of the policy. + +- Required: Yes +- Type: string + +### Parameter: `policies.threshold` + +The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy). + +- Required: Yes +- Type: string + +### Parameter: `policies.description` + +The description of the policy. + +- Required: No +- Type: string + +### Parameter: `policies.factData` + +The fact data of the policy. + +- Required: No +- Type: string + +### Parameter: `policies.status` + +The status of the policy. Default is "Enabled". + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `premiumDataDisks` + +The setting to enable usage of premium data disks. When its value is "Enabled", creation of standard or premium data disks is allowed. When its value is "Disabled", only creation of standard data disks is allowed. Default is "Disabled". + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `roleAssignments` + +Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `schedules` + +Schedules to create for the lab. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-schedulesname) | string | The name of the schedule. | +| [`taskType`](#parameter-schedulestasktype) | string | The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask). | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dailyRecurrence`](#parameter-schedulesdailyrecurrence) | object | The daily recurrence of the schedule. | +| [`hourlyRecurrence`](#parameter-scheduleshourlyrecurrence) | object | If the schedule will occur multiple times a day, specify the hourly recurrence. | +| [`notificationSettings`](#parameter-schedulesnotificationsettings) | object | The notification settings for the schedule. | +| [`status`](#parameter-schedulesstatus) | string | The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled". | +| [`tags`](#parameter-schedulestags) | object | The tags of the schedule. | +| [`targetResourceId`](#parameter-schedulestargetresourceid) | string | The resource ID to which the schedule belongs. | +| [`timeZoneId`](#parameter-schedulestimezoneid) | string | The time zone ID of the schedule. Defaults to "Pacific Standard time". | +| [`weeklyRecurrence`](#parameter-schedulesweeklyrecurrence) | object | If the schedule will occur only some days of the week, specify the weekly recurrence. | + +### Parameter: `schedules.name` + +The name of the schedule. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'LabVmAutoStart' + 'LabVmsShutdown' + ] + ``` + +### Parameter: `schedules.taskType` + +The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask). + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'LabVmsShutdownTask' + 'LabVmsStartupTask' + ] + ``` + +### Parameter: `schedules.dailyRecurrence` + +The daily recurrence of the schedule. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-schedulesdailyrecurrencetime) | string | The time of day the schedule will occur. | -**Optional parameters** +### Parameter: `schedules.dailyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `schedules.hourlyRecurrence` + +If the schedule will occur multiple times a day, specify the hourly recurrence. + +- Required: No +- Type: object + +**Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | -| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | -| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | -| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | -| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | +| [`minute`](#parameter-scheduleshourlyrecurrenceminute) | int | Minutes of the hour the schedule will run. | -### Parameter: `roleAssignments.principalId` +### Parameter: `schedules.hourlyRecurrence.minute` -The principal ID of the principal (user/group/identity) to assign the role to. +Minutes of the hour the schedule will run. - Required: Yes +- Type: int + +### Parameter: `schedules.notificationSettings` + +The notification settings for the schedule. + +- Required: No +- Type: object + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-schedulesnotificationsettingsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-schedulesnotificationsettingswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`notificationLocale`](#parameter-schedulesnotificationsettingsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`status`](#parameter-schedulesnotificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. | +| [`timeInMinutes`](#parameter-schedulesnotificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. | + +### Parameter: `schedules.notificationSettings.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No - Type: string -### Parameter: `roleAssignments.roleDefinitionIdOrName` +### Parameter: `schedules.notificationSettings.webHookUrl` -The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. -- Required: Yes +- Required: No - Type: string -### Parameter: `roleAssignments.condition` +### Parameter: `schedules.notificationSettings.notificationLocale` -The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". +The locale to use when sending a notification (fallback for unsupported languages is EN). - Required: No - Type: string -### Parameter: `roleAssignments.conditionVersion` +### Parameter: `schedules.notificationSettings.status` -Version of the condition. +If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. - Required: No - Type: string - Allowed: ```Bicep [ - '2.0' + 'Disabled' + 'Enabled' ] ``` -### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` +### Parameter: `schedules.notificationSettings.timeInMinutes` -The Resource Id of the delegated managed identity resource. +Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. + +- Required: No +- Type: int + +### Parameter: `schedules.status` + +The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled". - Required: No - Type: string +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` -### Parameter: `roleAssignments.description` +### Parameter: `schedules.tags` -The description of the role assignment. +The tags of the schedule. + +- Required: No +- Type: object + +### Parameter: `schedules.targetResourceId` + +The resource ID to which the schedule belongs. - Required: No - Type: string -### Parameter: `roleAssignments.principalType` +### Parameter: `schedules.timeZoneId` -The principal type of the assigned principal ID. +The time zone ID of the schedule. Defaults to "Pacific Standard time". - Required: No - Type: string -- Allowed: - ```Bicep - [ - 'Device' - 'ForeignGroup' - 'Group' - 'ServicePrincipal' - 'User' - ] - ``` -### Parameter: `schedules` +### Parameter: `schedules.weeklyRecurrence` -Schedules to create for the lab. +If the schedule will occur only some days of the week, specify the weekly recurrence. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-schedulesweeklyrecurrencetime) | string | The time of day the schedule will occur. | +| [`weekdays`](#parameter-schedulesweeklyrecurrenceweekdays) | array | The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). | + +### Parameter: `schedules.weeklyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `schedules.weeklyRecurrence.weekdays` + +The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). + +- Required: Yes - Type: array -- Default: `[]` ### Parameter: `support` @@ -1134,7 +1900,221 @@ Virtual networks to create for the lab. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`externalProviderResourceId`](#parameter-virtualnetworksexternalproviderresourceid) | string | The external provider resource ID of the virtual network. | +| [`name`](#parameter-virtualnetworksname) | string | The name of the virtual network. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedSubnets`](#parameter-virtualnetworksallowedsubnets) | array | The allowed subnets of the virtual network. | +| [`description`](#parameter-virtualnetworksdescription) | string | The description of the virtual network. | +| [`subnetOverrides`](#parameter-virtualnetworkssubnetoverrides) | array | The subnet overrides of the virtual network. | +| [`tags`](#parameter-virtualnetworkstags) | object | The tags of the virtual network. | + +### Parameter: `virtualnetworks.externalProviderResourceId` + +The external provider resource ID of the virtual network. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.name` + +The name of the virtual network. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets` + +The allowed subnets of the virtual network. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-virtualnetworksallowedsubnetslabsubnetname) | string | The name of the subnet as seen in the lab. | +| [`resourceId`](#parameter-virtualnetworksallowedsubnetsresourceid) | string | The resource ID of the allowed subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowPublicIp`](#parameter-virtualnetworksallowedsubnetsallowpublicip) | string | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | + +### Parameter: `virtualnetworks.allowedSubnets.labSubnetName` + +The name of the subnet as seen in the lab. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets.resourceId` + +The resource ID of the allowed subnet. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.allowedSubnets.allowPublicIp` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.description` + +The description of the virtual network. + +- Required: No +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides` + +The subnet overrides of the virtual network. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-virtualnetworkssubnetoverrideslabsubnetname) | string | The name given to the subnet within the lab. | +| [`resourceId`](#parameter-virtualnetworkssubnetoverridesresourceid) | string | The resource ID of the subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`sharedPublicIpAddressConfiguration`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfiguration) | object | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | +| [`useInVmCreationPermission`](#parameter-virtualnetworkssubnetoverridesuseinvmcreationpermission) | string | Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). | +| [`usePublicIpAddressPermission`](#parameter-virtualnetworkssubnetoverridesusepublicipaddresspermission) | string | Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). | +| [`virtualNetworkPoolName`](#parameter-virtualnetworkssubnetoverridesvirtualnetworkpoolname) | string | The virtual network pool associated with this subnet. | + +### Parameter: `virtualnetworks.subnetOverrides.labSubnetName` + +The name given to the subnet within the lab. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides.resourceId` + +The resource ID of the subnet. + +- Required: Yes +- Type: string + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedPorts`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedports) | array | Backend ports that virtual machines on this subnet are allowed to expose. | + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts` + +Backend ports that virtual machines on this subnet are allowed to expose. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`backendPort`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedportsbackendport) | int | Backend port of the target virtual machine. | +| [`transportProtocol`](#parameter-virtualnetworkssubnetoverridessharedpublicipaddressconfigurationallowedportstransportprotocol) | string | Protocol type of the port. | + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.backendPort` + +Backend port of the target virtual machine. + +- Required: Yes +- Type: int + +### Parameter: `virtualnetworks.subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.transportProtocol` + +Protocol type of the port. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Tcp' + 'Udp' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.useInVmCreationPermission` + +Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.usePublicIpAddressPermission` + +Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `virtualnetworks.subnetOverrides.virtualNetworkPoolName` + +The virtual network pool associated with this subnet. + +- Required: No +- Type: string + +### Parameter: `virtualnetworks.tags` + +The tags of the virtual network. + +- Required: No +- Type: object ### Parameter: `vmCreationResourceGroupId` diff --git a/avm/res/dev-test-lab/lab/artifactsource/README.md b/avm/res/dev-test-lab/lab/artifactsource/README.md index 317b5d0302..4499ea5096 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/README.md +++ b/avm/res/dev-test-lab/lab/artifactsource/README.md @@ -66,7 +66,6 @@ The folder containing Azure Resource Manager templates. Required if "folderPath" - Required: No - Type: string -- Default: `''` ### Parameter: `folderPath` @@ -74,7 +73,6 @@ The folder containing artifacts. At least one folder path is required. Required - Required: No - Type: string -- Default: `''` ### Parameter: `labName` @@ -89,7 +87,6 @@ The artifact source's branch reference (e.g. main or master). - Required: No - Type: string -- Default: `''` ### Parameter: `displayName` @@ -105,7 +102,6 @@ The security token to authenticate to the artifact source. - Required: No - Type: securestring -- Default: `''` ### Parameter: `sourceType` @@ -113,11 +109,9 @@ The artifact source's type. - Required: No - Type: string -- Default: `''` - Allowed: ```Bicep [ - '' 'GitHub' 'StorageAccount' 'VsoGit' diff --git a/avm/res/dev-test-lab/lab/artifactsource/main.bicep b/avm/res/dev-test-lab/lab/artifactsource/main.bicep index b350884783..cf9fecf54b 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/main.bicep +++ b/avm/res/dev-test-lab/lab/artifactsource/main.bicep @@ -17,26 +17,25 @@ param tags object? param displayName string = name @sys.description('Optional. The artifact source\'s branch reference (e.g. main or master).') -param branchRef string = '' +param branchRef string? @sys.description('Conditional. The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty.') -param folderPath string = '' +param folderPath string? @sys.description('Conditional. The folder containing Azure Resource Manager templates. Required if "folderPath" is empty.') -param armTemplateFolderPath string = '' +param armTemplateFolderPath string? @sys.description('Optional. The security token to authenticate to the artifact source.') @secure() -param securityToken string = '' +param securityToken string? @allowed([ - '' 'GitHub' 'StorageAccount' 'VsoGit' ]) @sys.description('Optional. The artifact source\'s type.') -param sourceType string = '' +param sourceType string? @allowed([ 'Enabled' @@ -58,11 +57,11 @@ resource artifactsource 'Microsoft.DevTestLab/labs/artifactsources@2018-09-15' = tags: tags properties: { displayName: displayName - branchRef: !empty(branchRef) ? branchRef : null - folderPath: !empty(folderPath) ? folderPath : null - armTemplateFolderPath: !empty(armTemplateFolderPath) ? armTemplateFolderPath : null - securityToken: !empty(securityToken) ? securityToken : null - sourceType: !empty(sourceType) ? sourceType : null + branchRef: branchRef + folderPath: folderPath + armTemplateFolderPath: armTemplateFolderPath + securityToken: securityToken + sourceType: sourceType status: status uri: uri } diff --git a/avm/res/dev-test-lab/lab/artifactsource/main.json b/avm/res/dev-test-lab/lab/artifactsource/main.json index be320c82ec..d6c775b85b 100644 --- a/avm/res/dev-test-lab/lab/artifactsource/main.json +++ b/avm/res/dev-test-lab/lab/artifactsource/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11935479410507680738" + "version": "0.29.47.4906", + "templateHash": "13904061272597362111" }, "name": "DevTest Lab Artifact Sources", - "description": "This module deploys a DevTest Lab Artifact Source.\n\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", + "description": "This module deploys a DevTest Lab Artifact Source.\r\n\r\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -41,37 +41,36 @@ }, "branchRef": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The artifact source's branch reference (e.g. main or master)." } }, "folderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." } }, "armTemplateFolderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." } }, "securityToken": { "type": "securestring", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The security token to authenticate to the artifact source." } }, "sourceType": { "type": "string", - "defaultValue": "", + "nullable": true, "allowedValues": [ - "", "GitHub", "StorageAccount", "VsoGit" @@ -112,11 +111,11 @@ "tags": "[parameters('tags')]", "properties": { "displayName": "[parameters('displayName')]", - "branchRef": "[if(not(empty(parameters('branchRef'))), parameters('branchRef'), null())]", - "folderPath": "[if(not(empty(parameters('folderPath'))), parameters('folderPath'), null())]", - "armTemplateFolderPath": "[if(not(empty(parameters('armTemplateFolderPath'))), parameters('armTemplateFolderPath'), null())]", - "securityToken": "[if(not(empty(parameters('securityToken'))), parameters('securityToken'), null())]", - "sourceType": "[if(not(empty(parameters('sourceType'))), parameters('sourceType'), null())]", + "branchRef": "[parameters('branchRef')]", + "folderPath": "[parameters('folderPath')]", + "armTemplateFolderPath": "[parameters('armTemplateFolderPath')]", + "securityToken": "[parameters('securityToken')]", + "sourceType": "[parameters('sourceType')]", "status": "[parameters('status')]", "uri": "[parameters('uri')]" }, diff --git a/avm/res/dev-test-lab/lab/cost/README.md b/avm/res/dev-test-lab/lab/cost/README.md index 084c7e069d..861224b60f 100644 --- a/avm/res/dev-test-lab/lab/cost/README.md +++ b/avm/res/dev-test-lab/lab/cost/README.md @@ -73,7 +73,6 @@ Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z) - Required: No - Type: string -- Default: `''` ### Parameter: `cycleStartDateTime` @@ -81,7 +80,6 @@ Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000 - Required: No - Type: string -- Default: `''` ### Parameter: `labName` diff --git a/avm/res/dev-test-lab/lab/cost/main.bicep b/avm/res/dev-test-lab/lab/cost/main.bicep index a380c409b0..0cd9280b88 100644 --- a/avm/res/dev-test-lab/lab/cost/main.bicep +++ b/avm/res/dev-test-lab/lab/cost/main.bicep @@ -18,10 +18,10 @@ param cycleType string param tags object? @sys.description('Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') -param cycleStartDateTime string = '' +param cycleStartDateTime string? @sys.description('Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') -param cycleEndDateTime string = '' +param cycleEndDateTime string? @allowed([ 'Enabled' diff --git a/avm/res/dev-test-lab/lab/cost/main.json b/avm/res/dev-test-lab/lab/cost/main.json index 725ab01ee8..ff19b73a67 100644 --- a/avm/res/dev-test-lab/lab/cost/main.json +++ b/avm/res/dev-test-lab/lab/cost/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11267524415855336246" + "version": "0.29.47.4906", + "templateHash": "7509251296299887127" }, "name": "DevTest Lab Costs", - "description": "This module deploys a DevTest Lab Cost.\n\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", + "description": "This module deploys a DevTest Lab Cost.\r\n\r\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -38,14 +38,14 @@ }, "cycleStartDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } }, "cycleEndDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } diff --git a/avm/res/dev-test-lab/lab/main.bicep b/avm/res/dev-test-lab/lab/main.bicep index 1723450ca7..3d0fe3a7ce 100644 --- a/avm/res/dev-test-lab/lab/main.bicep +++ b/avm/res/dev-test-lab/lab/main.bicep @@ -57,7 +57,7 @@ param premiumDataDisks string = 'Disabled' @description('Optional. The properties of any lab support message associated with this lab.') param support object = {} -@description('Optional. The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment.') +@description('Optional. The managed identity definition for this resource. For new labs created after 8/10/2020, the lab\'s system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab.') param managedIdentities managedIdentitiesType @description('Optional. The resource ID(s) to assign to the virtual machines associated with this lab.') @@ -94,22 +94,22 @@ param encryptionType string = 'EncryptionAtRestWithPlatformKey' param encryptionDiskEncryptionSetId string = '' @description('Optional. Virtual networks to create for the lab.') -param virtualnetworks array = [] +param virtualnetworks virtualNetworkType @description('Optional. Policies to create for the lab.') -param policies array = [] +param policies policiesType @description('Optional. Schedules to create for the lab.') -param schedules array = [] +param schedules scheduleType @description('Conditional. Notification Channels to create for the lab. Required if the schedules property "notificationSettingsStatus" is set to "Enabled.') -param notificationchannels array = [] +param notificationchannels notificationChannelType @description('Optional. Artifact sources to create for the lab.') -param artifactsources array = [] +param artifactsources artifactsourcesType @description('Optional. Costs to create for the lab.') -param costs object = {} +param costs costsType @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true @@ -125,7 +125,9 @@ var identity = !empty(managedIdentities) type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned' userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } - : any(null) + : { + type: 'SystemAssigned' + } var formattedManagementIdentities = !empty(managementIdentitiesResourceIds) ? reduce(map((managementIdentitiesResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) @@ -157,6 +159,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.devtestlab-lab.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -215,95 +228,86 @@ resource lab_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ? } module lab_virtualNetworks 'virtualnetwork/main.bicep' = [ - for (virtualNetwork, index) in virtualnetworks: { + for (virtualNetwork, index) in (virtualnetworks ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-VirtualNetwork-${index}' params: { labName: lab.name name: virtualNetwork.name tags: virtualNetwork.?tags ?? tags externalProviderResourceId: virtualNetwork.externalProviderResourceId - description: contains(virtualNetwork, 'description') ? virtualNetwork.description : '' - allowedSubnets: contains(virtualNetwork, 'allowedSubnets') ? virtualNetwork.allowedSubnets : [] - subnetOverrides: contains(virtualNetwork, 'subnetOverrides') ? virtualNetwork.subnetOverrides : [] + description: virtualNetwork.?description + allowedSubnets: virtualNetwork.?allowedSubnets + subnetOverrides: virtualNetwork.?subnetOverrides } } ] module lab_policies 'policyset/policy/main.bicep' = [ - for (policy, index) in policies: { + for (policy, index) in (policies ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-PolicySets-Policy-${index}' params: { labName: lab.name name: policy.name - - description: contains(policy, 'description') ? policy.description : '' + description: policy.?description evaluatorType: policy.evaluatorType - factData: contains(policy, 'factData') ? policy.factData : '' + factData: policy.?factData factName: policy.factName - status: contains(policy, 'status') ? policy.status : 'Enabled' + status: policy.?status ?? 'Enabled' threshold: policy.threshold } } ] module lab_schedules 'schedule/main.bicep' = [ - for (schedule, index) in schedules: { + for (schedule, index) in (schedules ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-Schedules-${index}' params: { labName: lab.name name: schedule.name tags: schedule.?tags ?? tags taskType: schedule.taskType - dailyRecurrence: contains(schedule, 'dailyRecurrence') ? schedule.dailyRecurrence : {} - hourlyRecurrence: contains(schedule, 'hourlyRecurrence') ? schedule.hourlyRecurrence : {} - weeklyRecurrence: contains(schedule, 'weeklyRecurrence') ? schedule.weeklyRecurrence : {} - status: contains(schedule, 'status') ? schedule.status : 'Enabled' - targetResourceId: contains(schedule, 'targetResourceId') ? schedule.targetResourceId : '' - timeZoneId: contains(schedule, 'timeZoneId') ? schedule.timeZoneId : 'Pacific Standard time' - notificationSettingsStatus: contains(schedule, 'notificationSettingsStatus') - ? schedule.notificationSettingsStatus - : 'Disabled' - notificationSettingsTimeInMinutes: contains(schedule, 'notificationSettingsTimeInMinutes') - ? schedule.notificationSettingsTimeInMinutes - : 30 + dailyRecurrence: schedule.?dailyRecurrence + hourlyRecurrence: schedule.?hourlyRecurrence + weeklyRecurrence: schedule.?weeklyRecurrence + status: schedule.?status ?? 'Enabled' + targetResourceId: schedule.?targetResourceId + timeZoneId: schedule.?timeZoneId ?? 'Pacific Standard time' + notificationSettings: schedule.?notificationSettings } } ] module lab_notificationChannels 'notificationchannel/main.bicep' = [ - for (notificationChannel, index) in notificationchannels: { + for (notificationChannel, index) in (notificationchannels ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-NotificationChannels-${index}' params: { labName: lab.name name: notificationChannel.name tags: notificationChannel.?tags ?? tags - description: contains(notificationChannel, 'description') ? notificationChannel.description : '' + description: notificationChannel.?description events: notificationChannel.events - emailRecipient: contains(notificationChannel, 'emailRecipient') ? notificationChannel.emailRecipient : '' - webHookUrl: contains(notificationChannel, 'webhookUrl') ? notificationChannel.webhookUrl : '' - notificationLocale: contains(notificationChannel, 'notificationLocale') - ? notificationChannel.notificationLocale - : 'en' + emailRecipient: notificationChannel.?emailRecipient + webHookUrl: notificationChannel.?webHookUrl + notificationLocale: notificationChannel.?notificationLocale ?? 'en' } } ] module lab_artifactSources 'artifactsource/main.bicep' = [ - for (artifactSource, index) in artifactsources: { + for (artifactSource, index) in (artifactsources ?? []): { name: '${uniqueString(deployment().name, location)}-Lab-ArtifactSources-${index}' params: { labName: lab.name name: artifactSource.name tags: artifactSource.?tags ?? tags - displayName: contains(artifactSource, 'displayName') ? artifactSource.displayName : artifactSource.name - branchRef: contains(artifactSource, 'branchRef') ? artifactSource.branchRef : '' - folderPath: contains(artifactSource, 'folderPath') ? artifactSource.folderPath : '' - armTemplateFolderPath: contains(artifactSource, 'armTemplateFolderPath') - ? artifactSource.armTemplateFolderPath - : '' - sourceType: contains(artifactSource, 'sourceType') ? artifactSource.sourceType : '' - status: contains(artifactSource, 'status') ? artifactSource.status : 'Enabled' + displayName: artifactSource.?displayName ?? artifactSource.name + branchRef: artifactSource.?branchRef + folderPath: artifactSource.?folderPath + armTemplateFolderPath: artifactSource.?armTemplateFolderPath + sourceType: artifactSource.?sourceType + status: artifactSource.?status ?? 'Enabled' uri: artifactSource.uri + securityToken: artifactSource.?securityToken } } ] @@ -313,54 +317,30 @@ module lab_costs 'cost/main.bicep' = if (!empty(costs)) { params: { labName: lab.name tags: costs.?tags ?? tags - currencyCode: contains(costs, 'currencyCode') ? costs.currencyCode : 'USD' - cycleType: costs.cycleType - cycleStartDateTime: contains(costs, 'cycleStartDateTime') ? costs.cycleStartDateTime : '' - cycleEndDateTime: contains(costs, 'cycleEndDateTime') ? costs.cycleEndDateTime : '' - status: contains(costs, 'status') ? costs.status : 'Enabled' - target: contains(costs, 'target') ? costs.target : 0 - thresholdValue25DisplayOnChart: contains(costs, 'thresholdValue25DisplayOnChart') - ? costs.thresholdValue25DisplayOnChart - : 'Disabled' - thresholdValue25SendNotificationWhenExceeded: contains(costs, 'thresholdValue25SendNotificationWhenExceeded') - ? costs.thresholdValue25SendNotificationWhenExceeded - : 'Disabled' - thresholdValue50DisplayOnChart: contains(costs, 'thresholdValue50DisplayOnChart') - ? costs.thresholdValue50DisplayOnChart - : 'Disabled' - thresholdValue50SendNotificationWhenExceeded: contains(costs, 'thresholdValue50SendNotificationWhenExceeded') - ? costs.thresholdValue50SendNotificationWhenExceeded - : 'Disabled' - thresholdValue75DisplayOnChart: contains(costs, 'thresholdValue75DisplayOnChart') - ? costs.thresholdValue75DisplayOnChart - : 'Disabled' - thresholdValue75SendNotificationWhenExceeded: contains(costs, 'thresholdValue75SendNotificationWhenExceeded') - ? costs.thresholdValue75SendNotificationWhenExceeded - : 'Disabled' - thresholdValue100DisplayOnChart: contains(costs, 'thresholdValue100DisplayOnChart') - ? costs.thresholdValue100DisplayOnChart - : 'Disabled' - thresholdValue100SendNotificationWhenExceeded: contains(costs, 'thresholdValue100SendNotificationWhenExceeded') - ? costs.thresholdValue100SendNotificationWhenExceeded - : 'Disabled' - thresholdValue125DisplayOnChart: contains(costs, 'thresholdValue125DisplayOnChart') - ? costs.thresholdValue125DisplayOnChart - : 'Disabled' - thresholdValue125SendNotificationWhenExceeded: contains(costs, 'thresholdValue125SendNotificationWhenExceeded') - ? costs.thresholdValue125SendNotificationWhenExceeded - : 'Disabled' + currencyCode: costs.?currencyCode ?? 'USD' + cycleType: costs!.cycleType + cycleStartDateTime: costs.?cycleStartDateTime + cycleEndDateTime: costs.?cycleEndDateTime + status: costs.?status ?? 'Enabled' + target: costs.?target ?? 0 + thresholdValue25DisplayOnChart: costs.?thresholdValue25DisplayOnChart ?? 'Disabled' + thresholdValue25SendNotificationWhenExceeded: costs.?thresholdValue25SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue50DisplayOnChart: costs.?thresholdValue50DisplayOnChart ?? 'Disabled' + thresholdValue50SendNotificationWhenExceeded: costs.?thresholdValue50SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue75DisplayOnChart: costs.?thresholdValue75DisplayOnChart ?? 'Disabled' + thresholdValue75SendNotificationWhenExceeded: costs.?thresholdValue75SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue100DisplayOnChart: costs.?thresholdValue100DisplayOnChart ?? 'Disabled' + thresholdValue100SendNotificationWhenExceeded: costs.?thresholdValue100SendNotificationWhenExceeded ?? 'Disabled' + thresholdValue125DisplayOnChart: costs.?thresholdValue125DisplayOnChart ?? 'Disabled' + thresholdValue125SendNotificationWhenExceeded: costs.?thresholdValue125SendNotificationWhenExceeded ?? 'Disabled' } } resource lab_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(lab.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(lab.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -408,6 +388,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -429,3 +412,199 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +type artifactsourcesType = { + @description('Required. The name of the artifact source.') + name: string + + @description('Optional. The tags of the artifact source.') + tags: object? + + @description('Optional. The display name of the artifact source. Default is the name of the artifact source.') + displayName: string? + + @description('Optional. The artifact source\'s branch reference (e.g. main or master).') + branchRef: string? + + @description('Conditional. The folder containing artifacts. At least one folder path is required. Required if "armTemplateFolderPath" is empty.') + folderPath: string? + + @description('Conditional. The folder containing Azure Resource Manager templates. Required if "folderPath" is empty.') + armTemplateFolderPath: string? + + @description('Optional. The artifact source\'s type.') + sourceType: 'GitHub' | 'StorageAccount' | 'VsoGit'? + + @description('Optional. Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is "Enabled".') + status: 'Enabled' | 'Disabled'? + + @description('Required. The artifact source\'s URI.') + uri: string + + @description('Optional. The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab\'s managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity.') + @secure() + securityToken: string? +}[]? + +import { allowedSubnetType, subnetOverrideType } from 'virtualnetwork/main.bicep' +type virtualNetworkType = { + @description('Required. The name of the virtual network.') + name: string + + @description('Optional. The tags of the virtual network.') + tags: object? + + @description('Required. The external provider resource ID of the virtual network.') + externalProviderResourceId: string + + @description('Optional. The description of the virtual network.') + description: string? + + @description('Optional. The allowed subnets of the virtual network.') + allowedSubnets: allowedSubnetType[]? + + @description('Optional. The subnet overrides of the virtual network.') + subnetOverrides: subnetOverrideType[]? +}[]? + +type costsType = { + @description('Optional. The tags of the resource.') + tags: object? + + @description('Required. Reporting cycle type.') + cycleType: 'Custom' | 'CalendarMonth' + + @description('Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') + cycleStartDateTime: string? + + @description('Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to "Custom".') + cycleEndDateTime: string? + + @description('Optional. Target cost status.') + status: 'Enabled' | 'Disabled'? + + @description('Optional. Lab target cost (e.g. 100). The target cost will appear in the "Cost trend" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds.') + target: int? + + @description('Optional. The currency code of the cost. Default is "USD".') + currencyCode: string? + + @description('Optional. Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue25DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue25SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue50DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue50SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue75DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue75SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue100DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue100SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? + + @description('Optional. Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts.') + thresholdValue125DisplayOnChart: 'Enabled' | 'Disabled'? + + @description('Optional. Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded.') + thresholdValue125SendNotificationWhenExceeded: 'Enabled' | 'Disabled'? +}? + +type notificationChannelType = { + @description('Required. The name of the notification channel.') + name: 'autoShutdown' | 'costThreshold' + + @description('Optional. The tags of the notification channel.') + tags: object? + + @description('Optional. The description of the notification.') + description: string? + + @description('Required. The list of event for which this notification is enabled. Can be "AutoShutdown" or "Cost".') + events: string[] + + @description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') + emailRecipient: string? + + @description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') + webHookUrl: string? + + @description('Optional. The locale to use when sending a notification (fallback for unsupported languages is EN).') + notificationLocale: string? +}[]? + +type policiesType = { + @description('Required. The name of the policy.') + name: string + + @description('Optional. The description of the policy.') + description: string? + + @description('Required. The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy).') + evaluatorType: 'AllowedValuesPolicy' | 'MaxValuePolicy' + + @description('Optional. The fact data of the policy.') + factData: string? + + @description('Required. The fact name of the policy.') + factName: + | 'EnvironmentTemplate' + | 'GalleryImage' + | 'LabPremiumVmCount' + | 'LabTargetCost' + | 'LabVmCount' + | 'LabVmSize' + | 'ScheduleEditPermission' + | 'UserOwnedLabPremiumVmCount' + | 'UserOwnedLabVmCount' + | 'UserOwnedLabVmCountInSubnet' + + @description('Optional. The status of the policy. Default is "Enabled".') + status: 'Disabled' | 'Enabled'? + + @description('Required. The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy).') + threshold: string +}[]? + +import { dailyRecurrenceType, hourlyRecurrenceType, notificationSettingsType, weeklyRecurrenceType } from 'schedule/main.bicep' +type scheduleType = { + @description('Required. The name of the schedule.') + name: 'LabVmsShutdown' | 'LabVmAutoStart' + + @description('Optional. The tags of the schedule.') + tags: object? + + @description('Required. The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask).') + taskType: 'LabVmsShutdownTask' | 'LabVmsStartupTask' + + @description('Optional. The daily recurrence of the schedule.') + dailyRecurrence: dailyRecurrenceType? + + @description('Optional. If the schedule will occur multiple times a day, specify the hourly recurrence.') + hourlyRecurrence: hourlyRecurrenceType? + + @description('Optional. If the schedule will occur only some days of the week, specify the weekly recurrence.') + weeklyRecurrence: weeklyRecurrenceType? + + @description('Optional. The status of the schedule (i.e. Enabled, Disabled). Default is "Enabled".') + status: 'Disabled' | 'Enabled'? + + @description('Optional. The resource ID to which the schedule belongs.') + targetResourceId: string? + + @description('Optional. The time zone ID of the schedule. Defaults to "Pacific Standard time".') + timeZoneId: string? + + @description('Optional. The notification settings for the schedule.') + notificationSettings: notificationSettingsType? +}[]? diff --git a/avm/res/dev-test-lab/lab/main.json b/avm/res/dev-test-lab/lab/main.json index e3b6286c55..711f287f08 100644 --- a/avm/res/dev-test-lab/lab/main.json +++ b/avm/res/dev-test-lab/lab/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3442844317076377471" + "version": "0.29.47.4906", + "templateHash": "9164793528274272981" }, "name": "DevTest Labs", "description": "This module deploys a DevTest Lab.", @@ -58,6 +58,13 @@ "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": { @@ -98,26 +105,797 @@ "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, - "conditionVersion": { - "type": "string", - "allowedValues": [ - "2.0" - ], - "nullable": true, - "metadata": { - "description": "Optional. Version of the condition." - } + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "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 + }, + "artifactsourcesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the artifact source." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the artifact source." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The display name of the artifact source. Default is the name of the artifact source." + } + }, + "branchRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The artifact source's branch reference (e.g. main or master)." + } + }, + "folderPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." + } + }, + "armTemplateFolderPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." + } + }, + "sourceType": { + "type": "string", + "allowedValues": [ + "GitHub", + "StorageAccount", + "VsoGit" + ], + "nullable": true, + "metadata": { + "description": "Optional. The artifact source's type." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates if the artifact source is enabled (values: Enabled, Disabled). Default is \"Enabled\"." + } + }, + "uri": { + "type": "string", + "metadata": { + "description": "Required. The artifact source's URI." + } + }, + "securityToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. The security token to authenticate to the artifact source. Private artifacts use the system-identity of the lab to store the security token for the artifact source in the lab's managed Azure Key Vault. Access to the Azure Key Vault is granted automatically only when the lab is created with a system-assigned identity." + } + } + } + }, + "nullable": true + }, + "virtualNetworkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual network." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the virtual network." + } + }, + "externalProviderResourceId": { + "type": "string", + "metadata": { + "description": "Required. The external provider resource ID of the virtual network." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the virtual network." + } + }, + "allowedSubnets": { + "type": "array", + "items": { + "$ref": "#/definitions/allowedSubnetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The allowed subnets of the virtual network." + } + }, + "subnetOverrides": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetOverrideType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The subnet overrides of the virtual network." + } + } + } + }, + "nullable": true + }, + "costsType": { + "type": "object", + "properties": { + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the resource." + } + }, + "cycleType": { + "type": "string", + "allowedValues": [ + "CalendarMonth", + "Custom" + ], + "metadata": { + "description": "Required. Reporting cycle type." + } + }, + "cycleStartDateTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." + } + }, + "cycleEndDateTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost status." + } + }, + "target": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Lab target cost (e.g. 100). The target cost will appear in the \"Cost trend\" chart to allow tracking lab spending relative to the target cost for the current reporting cycleSetting the target cost to 0 will disable all thresholds." + } + }, + "currencyCode": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The currency code of the cost. Default is \"USD\"." + } + }, + "thresholdValue25DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 25% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue25SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 25% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue50DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 50% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue50SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 50% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue75DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 75% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue75SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 75% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue100DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 100% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue100SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 100% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + }, + "thresholdValue125DisplayOnChart": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target Cost threshold at 125% display on chart. Indicates whether this threshold will be displayed on cost charts." + } + }, + "thresholdValue125SendNotificationWhenExceeded": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Target cost threshold at 125% send notification when exceeded. Indicates whether notifications will be sent when this threshold is exceeded." + } + } + }, + "nullable": true + }, + "notificationChannelType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "autoShutdown", + "costThreshold" + ], + "metadata": { + "description": "Required. The name of the notification channel." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the notification channel." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the notification." + } + }, + "events": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of event for which this notification is enabled. Can be \"AutoShutdown\" or \"Cost\"." + } + }, + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + } + } + }, + "nullable": true + }, + "policiesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the policy." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the policy." + } + }, + "evaluatorType": { + "type": "string", + "allowedValues": [ + "AllowedValuesPolicy", + "MaxValuePolicy" + ], + "metadata": { + "description": "Required. The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy)." + } + }, + "factData": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The fact data of the policy." + } + }, + "factName": { + "type": "string", + "allowedValues": [ + "EnvironmentTemplate", + "GalleryImage", + "LabPremiumVmCount", + "LabTargetCost", + "LabVmCount", + "LabVmSize", + "ScheduleEditPermission", + "UserOwnedLabPremiumVmCount", + "UserOwnedLabVmCount", + "UserOwnedLabVmCountInSubnet" + ], + "metadata": { + "description": "Required. The fact name of the policy." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the policy. Default is \"Enabled\"." + } + }, + "threshold": { + "type": "string", + "metadata": { + "description": "Required. The threshold of the policy (i.e. a number for MaxValuePolicy, and a JSON array of values for AllowedValuesPolicy)." + } + } + } + }, + "nullable": true + }, + "scheduleType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "LabVmAutoStart", + "LabVmsShutdown" + ], + "metadata": { + "description": "Required. The name of the schedule." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the schedule." + } + }, + "taskType": { + "type": "string", + "allowedValues": [ + "LabVmsShutdownTask", + "LabVmsStartupTask" + ], + "metadata": { + "description": "Required. The task type of the schedule (e.g. LabVmsShutdownTask, LabVmsStartupTask)." + } + }, + "dailyRecurrence": { + "$ref": "#/definitions/dailyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. The daily recurrence of the schedule." + } + }, + "hourlyRecurrence": { + "$ref": "#/definitions/hourlyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." + } + }, + "weeklyRecurrence": { + "$ref": "#/definitions/weeklyRecurrenceType", + "nullable": true, + "metadata": { + "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the schedule (i.e. Enabled, Disabled). Default is \"Enabled\"." + } + }, + "targetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID to which the schedule belongs." + } + }, + "timeZoneId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time zone ID of the schedule. Defaults to \"Pacific Standard time\"." + } + }, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The notification settings for the schedule." + } + } + } + }, + "nullable": true + }, + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "virtualnetwork/main.bicep" + } + } + }, + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "virtualnetwork/main.bicep" + } + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" }, - "delegatedManagedIdentityResourceId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The Resource Id of the delegated managed identity resource." - } + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." } } }, - "nullable": true + "nullable": true, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "schedule/main.bicep" + } + } } }, "parameters": { @@ -232,7 +1010,7 @@ "managedIdentities": { "$ref": "#/definitions/managedIdentitiesType", "metadata": { - "description": "Optional. The managed identity definition for this resource. DevTest Labs creates a system-assigned identity by default the first time it creates the lab environment." + "description": "Optional. The managed identity definition for this resource. For new labs created after 8/10/2020, the lab's system assigned identity is set to On by default and lab owner will not be able to turn this off for the lifecycle of the lab." } }, "managementIdentitiesResourceIds": { @@ -300,43 +1078,37 @@ } }, "virtualnetworks": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/virtualNetworkType", "metadata": { "description": "Optional. Virtual networks to create for the lab." } }, "policies": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/policiesType", "metadata": { "description": "Optional. Policies to create for the lab." } }, "schedules": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/scheduleType", "metadata": { "description": "Optional. Schedules to create for the lab." } }, "notificationchannels": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/notificationChannelType", "metadata": { "description": "Conditional. Notification Channels to create for the lab. Required if the schedules property \"notificationSettingsStatus\" is set to \"Enabled." } }, "artifactsources": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/artifactsourcesType", "metadata": { "description": "Optional. Artifact sources to create for the lab." } }, "costs": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/costsType", "metadata": { "description": "Optional. Costs to create for the lab." } @@ -350,8 +1122,15 @@ } }, "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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), createObject('type', 'SystemAssigned'))]", "formattedManagementIdentities": "[if(not(empty(parameters('managementIdentitiesResourceIds'))), reduce(map(coalesce(parameters('managementIdentitiesResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next')))), createObject())]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -430,20 +1209,20 @@ "lab_roleAssignments": { "copy": { "name": "lab_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DevTestLab/labs/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DevTestLab/labs', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DevTestLab/labs', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "lab" @@ -452,7 +1231,7 @@ "lab_virtualNetworks": { "copy": { "name": "lab_virtualNetworks", - "count": "[length(parameters('virtualnetworks'))]" + "count": "[length(coalesce(parameters('virtualnetworks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -467,17 +1246,23 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('virtualnetworks')[copyIndex()].name]" + "value": "[coalesce(parameters('virtualnetworks'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('virtualnetworks')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "externalProviderResourceId": { - "value": "[parameters('virtualnetworks')[copyIndex()].externalProviderResourceId]" + "value": "[coalesce(parameters('virtualnetworks'), createArray())[copyIndex()].externalProviderResourceId]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'description')]" + }, + "allowedSubnets": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'allowedSubnets')]" }, - "description": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'description'), createObject('value', parameters('virtualnetworks')[copyIndex()].description), createObject('value', ''))]", - "allowedSubnets": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'allowedSubnets'), createObject('value', parameters('virtualnetworks')[copyIndex()].allowedSubnets), createObject('value', createArray()))]", - "subnetOverrides": "[if(contains(parameters('virtualnetworks')[copyIndex()], 'subnetOverrides'), createObject('value', parameters('virtualnetworks')[copyIndex()].subnetOverrides), createObject('value', createArray()))]" + "subnetOverrides": { + "value": "[tryGet(coalesce(parameters('virtualnetworks'), createArray())[copyIndex()], 'subnetOverrides')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -486,13 +1271,133 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17468377090212462449" + "version": "0.29.47.4906", + "templateHash": "18309816581107210302" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -596,7 +1501,7 @@ "lab_policies": { "copy": { "name": "lab_policies", - "count": "[length(parameters('policies'))]" + "count": "[length(coalesce(parameters('policies'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -611,19 +1516,25 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('policies')[copyIndex()].name]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].name]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'description')]" }, - "description": "[if(contains(parameters('policies')[copyIndex()], 'description'), createObject('value', parameters('policies')[copyIndex()].description), createObject('value', ''))]", "evaluatorType": { - "value": "[parameters('policies')[copyIndex()].evaluatorType]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].evaluatorType]" + }, + "factData": { + "value": "[tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'factData')]" }, - "factData": "[if(contains(parameters('policies')[copyIndex()], 'factData'), createObject('value', parameters('policies')[copyIndex()].factData), createObject('value', ''))]", "factName": { - "value": "[parameters('policies')[copyIndex()].factName]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].factName]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('policies'), createArray())[copyIndex()], 'status'), 'Enabled')]" }, - "status": "[if(contains(parameters('policies')[copyIndex()], 'status'), createObject('value', parameters('policies')[copyIndex()].status), createObject('value', 'Enabled'))]", "threshold": { - "value": "[parameters('policies')[copyIndex()].threshold]" + "value": "[coalesce(parameters('policies'), createArray())[copyIndex()].threshold]" } }, "template": { @@ -632,11 +1543,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1969174363345800530" + "version": "0.29.47.4906", + "templateHash": "10307787353498465860" }, "name": "DevTest Lab Policy Sets Policies", - "description": "This module deploys a DevTest Lab Policy Sets Policy.\n\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", + "description": "This module deploys a DevTest Lab Policy Sets Policy.\r\n\r\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -718,9 +1629,9 @@ "apiVersion": "2018-09-15", "name": "[format('{0}/{1}/{2}', parameters('labName'), 'default', parameters('name'))]", "properties": { - "description": "[parameters('description')]", + "description": "[coalesce(parameters('description'), '')]", "evaluatorType": "[parameters('evaluatorType')]", - "factData": "[parameters('factData')]", + "factData": "[coalesce(parameters('factData'), '')]", "factName": "[parameters('factName')]", "status": "[parameters('status')]", "threshold": "[parameters('threshold')]" @@ -759,7 +1670,7 @@ "lab_schedules": { "copy": { "name": "lab_schedules", - "count": "[length(parameters('schedules'))]" + "count": "[length(coalesce(parameters('schedules'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -774,22 +1685,35 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('schedules')[copyIndex()].name]" + "value": "[coalesce(parameters('schedules'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('schedules')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "taskType": { - "value": "[parameters('schedules')[copyIndex()].taskType]" - }, - "dailyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'dailyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].dailyRecurrence), createObject('value', createObject()))]", - "hourlyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'hourlyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].hourlyRecurrence), createObject('value', createObject()))]", - "weeklyRecurrence": "[if(contains(parameters('schedules')[copyIndex()], 'weeklyRecurrence'), createObject('value', parameters('schedules')[copyIndex()].weeklyRecurrence), createObject('value', createObject()))]", - "status": "[if(contains(parameters('schedules')[copyIndex()], 'status'), createObject('value', parameters('schedules')[copyIndex()].status), createObject('value', 'Enabled'))]", - "targetResourceId": "[if(contains(parameters('schedules')[copyIndex()], 'targetResourceId'), createObject('value', parameters('schedules')[copyIndex()].targetResourceId), createObject('value', ''))]", - "timeZoneId": "[if(contains(parameters('schedules')[copyIndex()], 'timeZoneId'), createObject('value', parameters('schedules')[copyIndex()].timeZoneId), createObject('value', 'Pacific Standard time'))]", - "notificationSettingsStatus": "[if(contains(parameters('schedules')[copyIndex()], 'notificationSettingsStatus'), createObject('value', parameters('schedules')[copyIndex()].notificationSettingsStatus), createObject('value', 'Disabled'))]", - "notificationSettingsTimeInMinutes": "[if(contains(parameters('schedules')[copyIndex()], 'notificationSettingsTimeInMinutes'), createObject('value', parameters('schedules')[copyIndex()].notificationSettingsTimeInMinutes), createObject('value', 30))]" + "value": "[coalesce(parameters('schedules'), createArray())[copyIndex()].taskType]" + }, + "dailyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'dailyRecurrence')]" + }, + "hourlyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'hourlyRecurrence')]" + }, + "weeklyRecurrence": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'weeklyRecurrence')]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'status'), 'Enabled')]" + }, + "targetResourceId": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'targetResourceId')]" + }, + "timeZoneId": { + "value": "[coalesce(tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'timeZoneId'), 'Pacific Standard time')]" + }, + "notificationSettings": { + "value": "[tryGet(coalesce(parameters('schedules'), createArray())[copyIndex()], 'notificationSettings')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -798,13 +1722,117 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2093366910249957264" + "version": "0.29.47.4906", + "templateHash": "9010276477624635732" }, "name": "DevTest Lab Schedules", - "description": "This module deploys a DevTest Lab Schedule.\n\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", + "description": "This module deploys a DevTest Lab Schedule.\r\n\r\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -840,22 +1868,19 @@ } }, "dailyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/dailyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur once each day of the week, specify the daily recurrence." } }, "hourlyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/hourlyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." } }, "weeklyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/weeklyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." } @@ -873,7 +1898,7 @@ }, "targetResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID to which the schedule belongs." } @@ -885,22 +1910,10 @@ "description": "Optional. The time zone ID (e.g. Pacific Standard time)." } }, - "notificationSettingsStatus": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled)." - } - }, - "notificationSettingsTimeInMinutes": { - "type": "int", - "defaultValue": 30, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", "metadata": { - "description": "Optional. Time in minutes before event at which notification will be sent. Optional if \"notificationSettingsStatus\" is set to \"Enabled\". Default is 30 minutes." + "description": "Optional. The notification settings for the schedule." } } }, @@ -918,13 +1931,13 @@ "tags": "[parameters('tags')]", "properties": { "taskType": "[parameters('taskType')]", - "dailyRecurrence": "[if(not(empty(parameters('dailyRecurrence'))), parameters('dailyRecurrence'), null())]", - "hourlyRecurrence": "[if(not(empty(parameters('hourlyRecurrence'))), parameters('hourlyRecurrence'), null())]", - "weeklyRecurrence": "[if(not(empty(parameters('weeklyRecurrence'))), parameters('weeklyRecurrence'), null())]", + "dailyRecurrence": "[parameters('dailyRecurrence')]", + "hourlyRecurrence": "[parameters('hourlyRecurrence')]", + "weeklyRecurrence": "[parameters('weeklyRecurrence')]", "status": "[parameters('status')]", - "targetResourceId": "[if(not(empty(parameters('targetResourceId'))), parameters('targetResourceId'), null())]", + "targetResourceId": "[parameters('targetResourceId')]", "timeZoneId": "[parameters('timeZoneId')]", - "notificationSettings": "[if(equals(parameters('notificationSettingsStatus'), 'Enabled'), createObject('status', parameters('notificationSettingsStatus'), 'timeInMinutes', parameters('notificationSettingsTimeInMinutes')), createObject())]" + "notificationSettings": "[parameters('notificationSettings')]" }, "dependsOn": [ "lab" @@ -963,7 +1976,7 @@ "lab_notificationChannels": { "copy": { "name": "lab_notificationChannels", - "count": "[length(parameters('notificationchannels'))]" + "count": "[length(coalesce(parameters('notificationchannels'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -978,18 +1991,26 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('notificationchannels')[copyIndex()].name]" + "value": "[coalesce(parameters('notificationchannels'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('notificationchannels')[copyIndex()], 'tags'), parameters('tags'))]" + "value": "[coalesce(tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'description')]" }, - "description": "[if(contains(parameters('notificationchannels')[copyIndex()], 'description'), createObject('value', parameters('notificationchannels')[copyIndex()].description), createObject('value', ''))]", "events": { - "value": "[parameters('notificationchannels')[copyIndex()].events]" + "value": "[coalesce(parameters('notificationchannels'), createArray())[copyIndex()].events]" + }, + "emailRecipient": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'emailRecipient')]" }, - "emailRecipient": "[if(contains(parameters('notificationchannels')[copyIndex()], 'emailRecipient'), createObject('value', parameters('notificationchannels')[copyIndex()].emailRecipient), createObject('value', ''))]", - "webHookUrl": "[if(contains(parameters('notificationchannels')[copyIndex()], 'webhookUrl'), createObject('value', parameters('notificationchannels')[copyIndex()].webhookUrl), createObject('value', ''))]", - "notificationLocale": "[if(contains(parameters('notificationchannels')[copyIndex()], 'notificationLocale'), createObject('value', parameters('notificationchannels')[copyIndex()].notificationLocale), createObject('value', 'en'))]" + "webHookUrl": { + "value": "[tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'webHookUrl')]" + }, + "notificationLocale": { + "value": "[coalesce(tryGet(coalesce(parameters('notificationchannels'), createArray())[copyIndex()], 'notificationLocale'), 'en')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -998,11 +2019,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2288447095016894613" + "version": "0.29.47.4906", + "templateHash": "95632191903979650" }, "name": "DevTest Lab Notification Channels", - "description": "This module deploys a DevTest Lab Notification Channel.\n\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", + "description": "This module deploys a DevTest Lab Notification Channel.\r\n\r\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1045,7 +2066,7 @@ }, "emailRecipient": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." } @@ -1078,8 +2099,16 @@ "name": "[format('{0}/{1}', parameters('labName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { + "copy": [ + { + "name": "events", + "count": "[length(parameters('events'))]", + "input": { + "eventName": "[parameters('events')[copyIndex('events')]]" + } + } + ], "description": "[parameters('description')]", - "events": "[parameters('events')]", "emailRecipient": "[parameters('emailRecipient')]", "webHookUrl": "[parameters('webHookUrl')]", "notificationLocale": "[parameters('notificationLocale')]" @@ -1121,7 +2150,7 @@ "lab_artifactSources": { "copy": { "name": "lab_artifactSources", - "count": "[length(parameters('artifactsources'))]" + "count": "[length(coalesce(parameters('artifactsources'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -1136,19 +2165,34 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('artifactsources')[copyIndex()].name]" + "value": "[coalesce(parameters('artifactsources'), createArray())[copyIndex()].name]" }, "tags": { - "value": "[coalesce(tryGet(parameters('artifactsources')[copyIndex()], 'tags'), parameters('tags'))]" - }, - "displayName": "[if(contains(parameters('artifactsources')[copyIndex()], 'displayName'), createObject('value', parameters('artifactsources')[copyIndex()].displayName), createObject('value', parameters('artifactsources')[copyIndex()].name))]", - "branchRef": "[if(contains(parameters('artifactsources')[copyIndex()], 'branchRef'), createObject('value', parameters('artifactsources')[copyIndex()].branchRef), createObject('value', ''))]", - "folderPath": "[if(contains(parameters('artifactsources')[copyIndex()], 'folderPath'), createObject('value', parameters('artifactsources')[copyIndex()].folderPath), createObject('value', ''))]", - "armTemplateFolderPath": "[if(contains(parameters('artifactsources')[copyIndex()], 'armTemplateFolderPath'), createObject('value', parameters('artifactsources')[copyIndex()].armTemplateFolderPath), createObject('value', ''))]", - "sourceType": "[if(contains(parameters('artifactsources')[copyIndex()], 'sourceType'), createObject('value', parameters('artifactsources')[copyIndex()].sourceType), createObject('value', ''))]", - "status": "[if(contains(parameters('artifactsources')[copyIndex()], 'status'), createObject('value', parameters('artifactsources')[copyIndex()].status), createObject('value', 'Enabled'))]", + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "displayName": { + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'displayName'), coalesce(parameters('artifactsources'), createArray())[copyIndex()].name)]" + }, + "branchRef": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'branchRef')]" + }, + "folderPath": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'folderPath')]" + }, + "armTemplateFolderPath": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'armTemplateFolderPath')]" + }, + "sourceType": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'sourceType')]" + }, + "status": { + "value": "[coalesce(tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'status'), 'Enabled')]" + }, "uri": { - "value": "[parameters('artifactsources')[copyIndex()].uri]" + "value": "[coalesce(parameters('artifactsources'), createArray())[copyIndex()].uri]" + }, + "securityToken": { + "value": "[tryGet(coalesce(parameters('artifactsources'), createArray())[copyIndex()], 'securityToken')]" } }, "template": { @@ -1158,11 +2202,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5806826104969666329" + "version": "0.29.47.4906", + "templateHash": "13904061272597362111" }, "name": "DevTest Lab Artifact Sources", - "description": "This module deploys a DevTest Lab Artifact Source.\n\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", + "description": "This module deploys a DevTest Lab Artifact Source.\r\n\r\nAn artifact source allows you to create custom artifacts for the VMs in the lab, or use Azure Resource Manager templates to create a custom test environment. You must add a private Git repository for the artifacts or Resource Manager templates that your team creates. The repository can be hosted on GitHub or on Azure DevOps Services.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1194,37 +2238,36 @@ }, "branchRef": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The artifact source's branch reference (e.g. main or master)." } }, "folderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing artifacts. At least one folder path is required. Required if \"armTemplateFolderPath\" is empty." } }, "armTemplateFolderPath": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The folder containing Azure Resource Manager templates. Required if \"folderPath\" is empty." } }, "securityToken": { "type": "securestring", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The security token to authenticate to the artifact source." } }, "sourceType": { "type": "string", - "defaultValue": "", + "nullable": true, "allowedValues": [ - "", "GitHub", "StorageAccount", "VsoGit" @@ -1265,11 +2308,11 @@ "tags": "[parameters('tags')]", "properties": { "displayName": "[parameters('displayName')]", - "branchRef": "[if(not(empty(parameters('branchRef'))), parameters('branchRef'), null())]", - "folderPath": "[if(not(empty(parameters('folderPath'))), parameters('folderPath'), null())]", - "armTemplateFolderPath": "[if(not(empty(parameters('armTemplateFolderPath'))), parameters('armTemplateFolderPath'), null())]", - "securityToken": "[if(not(empty(parameters('securityToken'))), parameters('securityToken'), null())]", - "sourceType": "[if(not(empty(parameters('sourceType'))), parameters('sourceType'), null())]", + "branchRef": "[parameters('branchRef')]", + "folderPath": "[parameters('folderPath')]", + "armTemplateFolderPath": "[parameters('armTemplateFolderPath')]", + "securityToken": "[parameters('securityToken')]", + "sourceType": "[parameters('sourceType')]", "status": "[parameters('status')]", "uri": "[parameters('uri')]" }, @@ -1324,24 +2367,54 @@ "tags": { "value": "[coalesce(tryGet(parameters('costs'), 'tags'), parameters('tags'))]" }, - "currencyCode": "[if(contains(parameters('costs'), 'currencyCode'), createObject('value', parameters('costs').currencyCode), createObject('value', 'USD'))]", + "currencyCode": { + "value": "[coalesce(tryGet(parameters('costs'), 'currencyCode'), 'USD')]" + }, "cycleType": { "value": "[parameters('costs').cycleType]" }, - "cycleStartDateTime": "[if(contains(parameters('costs'), 'cycleStartDateTime'), createObject('value', parameters('costs').cycleStartDateTime), createObject('value', ''))]", - "cycleEndDateTime": "[if(contains(parameters('costs'), 'cycleEndDateTime'), createObject('value', parameters('costs').cycleEndDateTime), createObject('value', ''))]", - "status": "[if(contains(parameters('costs'), 'status'), createObject('value', parameters('costs').status), createObject('value', 'Enabled'))]", - "target": "[if(contains(parameters('costs'), 'target'), createObject('value', parameters('costs').target), createObject('value', 0))]", - "thresholdValue25DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue25DisplayOnChart'), createObject('value', parameters('costs').thresholdValue25DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue25SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue25SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue25SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue50DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue50DisplayOnChart'), createObject('value', parameters('costs').thresholdValue50DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue50SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue50SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue50SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue75DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue75DisplayOnChart'), createObject('value', parameters('costs').thresholdValue75DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue75SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue75SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue75SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue100DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue100DisplayOnChart'), createObject('value', parameters('costs').thresholdValue100DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue100SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue100SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue100SendNotificationWhenExceeded), createObject('value', 'Disabled'))]", - "thresholdValue125DisplayOnChart": "[if(contains(parameters('costs'), 'thresholdValue125DisplayOnChart'), createObject('value', parameters('costs').thresholdValue125DisplayOnChart), createObject('value', 'Disabled'))]", - "thresholdValue125SendNotificationWhenExceeded": "[if(contains(parameters('costs'), 'thresholdValue125SendNotificationWhenExceeded'), createObject('value', parameters('costs').thresholdValue125SendNotificationWhenExceeded), createObject('value', 'Disabled'))]" + "cycleStartDateTime": { + "value": "[tryGet(parameters('costs'), 'cycleStartDateTime')]" + }, + "cycleEndDateTime": { + "value": "[tryGet(parameters('costs'), 'cycleEndDateTime')]" + }, + "status": { + "value": "[coalesce(tryGet(parameters('costs'), 'status'), 'Enabled')]" + }, + "target": { + "value": "[coalesce(tryGet(parameters('costs'), 'target'), 0)]" + }, + "thresholdValue25DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue25DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue25SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue25SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue50DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue50DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue50SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue50SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue75DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue75DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue75SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue75SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue100DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue100DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue100SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue100SendNotificationWhenExceeded'), 'Disabled')]" + }, + "thresholdValue125DisplayOnChart": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue125DisplayOnChart'), 'Disabled')]" + }, + "thresholdValue125SendNotificationWhenExceeded": { + "value": "[coalesce(tryGet(parameters('costs'), 'thresholdValue125SendNotificationWhenExceeded'), 'Disabled')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1350,11 +2423,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6833769592570374423" + "version": "0.29.47.4906", + "templateHash": "7509251296299887127" }, "name": "DevTest Lab Costs", - "description": "This module deploys a DevTest Lab Cost.\n\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", + "description": "This module deploys a DevTest Lab Cost.\r\n\r\nManage lab costs by setting a spending target that can be viewed in the Monthly Estimated Cost Trend chart. DevTest Labs can send a notification when spending reaches the specified target threshold.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -1383,14 +2456,14 @@ }, "cycleStartDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle start date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } }, "cycleEndDateTime": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. Reporting cycle end date in the zulu time format (e.g. 2023-12-01T00:00:00.000Z). Required if cycleType is set to \"Custom\"." } diff --git a/avm/res/dev-test-lab/lab/notificationchannel/README.md b/avm/res/dev-test-lab/lab/notificationchannel/README.md index 5ab3d187aa..9fee767321 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/README.md +++ b/avm/res/dev-test-lab/lab/notificationchannel/README.md @@ -71,7 +71,6 @@ The email recipient to send notifications to (can be a list of semi-colon separa - Required: No - Type: string -- Default: `''` ### Parameter: `labName` diff --git a/avm/res/dev-test-lab/lab/notificationchannel/main.bicep b/avm/res/dev-test-lab/lab/notificationchannel/main.bicep index 2e97b66ddd..84fa6aa9f8 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/main.bicep +++ b/avm/res/dev-test-lab/lab/notificationchannel/main.bicep @@ -24,7 +24,7 @@ param description string = '' param events array = [] @sys.description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') -param emailRecipient string = '' +param emailRecipient string? @sys.description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') param webHookUrl string = '' @@ -42,7 +42,11 @@ resource notificationChannel 'Microsoft.DevTestLab/labs/notificationchannels@201 tags: tags properties: { description: description - events: events + events: [ + for event in events: { + eventName: event + } + ] emailRecipient: emailRecipient webHookUrl: webHookUrl notificationLocale: notificationLocale diff --git a/avm/res/dev-test-lab/lab/notificationchannel/main.json b/avm/res/dev-test-lab/lab/notificationchannel/main.json index 8d69509f11..36963586b6 100644 --- a/avm/res/dev-test-lab/lab/notificationchannel/main.json +++ b/avm/res/dev-test-lab/lab/notificationchannel/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1696945877581040577" + "version": "0.29.47.4906", + "templateHash": "95632191903979650" }, "name": "DevTest Lab Notification Channels", - "description": "This module deploys a DevTest Lab Notification Channel.\n\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", + "description": "This module deploys a DevTest Lab Notification Channel.\r\n\r\nNotification channels are used by the schedule resource type in order to send notifications or events to email addresses and/or webhooks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -52,7 +52,7 @@ }, "emailRecipient": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." } @@ -85,8 +85,16 @@ "name": "[format('{0}/{1}', parameters('labName'), parameters('name'))]", "tags": "[parameters('tags')]", "properties": { + "copy": [ + { + "name": "events", + "count": "[length(parameters('events'))]", + "input": { + "eventName": "[parameters('events')[copyIndex('events')]]" + } + } + ], "description": "[parameters('description')]", - "events": "[parameters('events')]", "emailRecipient": "[parameters('emailRecipient')]", "webHookUrl": "[parameters('webHookUrl')]", "notificationLocale": "[parameters('notificationLocale')]" diff --git a/avm/res/dev-test-lab/lab/policyset/policy/main.bicep b/avm/res/dev-test-lab/lab/policyset/policy/main.bicep index c9dbe3dba8..a4a53a50c4 100644 --- a/avm/res/dev-test-lab/lab/policyset/policy/main.bicep +++ b/avm/res/dev-test-lab/lab/policyset/policy/main.bicep @@ -60,9 +60,9 @@ resource policy 'Microsoft.DevTestLab/labs/policysets/policies@2018-09-15' = { name: name parent: lab::policySets properties: { - description: description + description: description ?? '' evaluatorType: evaluatorType - factData: factData + factData: factData ?? '' factName: factName status: status threshold: threshold diff --git a/avm/res/dev-test-lab/lab/policyset/policy/main.json b/avm/res/dev-test-lab/lab/policyset/policy/main.json index 6da1dd875f..35370542da 100644 --- a/avm/res/dev-test-lab/lab/policyset/policy/main.json +++ b/avm/res/dev-test-lab/lab/policyset/policy/main.json @@ -4,11 +4,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "486340638220018026" + "version": "0.29.47.4906", + "templateHash": "10307787353498465860" }, "name": "DevTest Lab Policy Sets Policies", - "description": "This module deploys a DevTest Lab Policy Sets Policy.\n\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", + "description": "This module deploys a DevTest Lab Policy Sets Policy.\r\n\r\nDevTest lab policies are used to modify the lab settings such as only allowing certain VM Size SKUs, marketplace image types, number of VMs allowed per user and other settings.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -90,9 +90,9 @@ "apiVersion": "2018-09-15", "name": "[format('{0}/{1}/{2}', parameters('labName'), 'default', parameters('name'))]", "properties": { - "description": "[parameters('description')]", + "description": "[coalesce(parameters('description'), '')]", "evaluatorType": "[parameters('evaluatorType')]", - "factData": "[parameters('factData')]", + "factData": "[coalesce(parameters('factData'), '')]", "factName": "[parameters('factName')]", "status": "[parameters('status')]", "threshold": "[parameters('threshold')]" diff --git a/avm/res/dev-test-lab/lab/schedule/README.md b/avm/res/dev-test-lab/lab/schedule/README.md index dda473fdde..2358f8c145 100644 --- a/avm/res/dev-test-lab/lab/schedule/README.md +++ b/avm/res/dev-test-lab/lab/schedule/README.md @@ -39,8 +39,7 @@ Lab schedules are used to modify the settings for auto-shutdown, auto-start for | :-- | :-- | :-- | | [`dailyRecurrence`](#parameter-dailyrecurrence) | object | If the schedule will occur once each day of the week, specify the daily recurrence. | | [`hourlyRecurrence`](#parameter-hourlyrecurrence) | object | If the schedule will occur multiple times a day, specify the hourly recurrence. | -| [`notificationSettingsStatus`](#parameter-notificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). | -| [`notificationSettingsTimeInMinutes`](#parameter-notificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes. | +| [`notificationSettings`](#parameter-notificationsettings) | object | The notification settings for the schedule. | | [`status`](#parameter-status) | string | The status of the schedule (i.e. Enabled, Disabled). | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`targetResourceId`](#parameter-targetresourceid) | string | The resource ID to which the schedule belongs. | @@ -88,7 +87,19 @@ If the schedule will occur once each day of the week, specify the daily recurren - Required: No - Type: object -- Default: `{}` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-dailyrecurrencetime) | string | The time of day the schedule will occur. | + +### Parameter: `dailyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string ### Parameter: `hourlyRecurrence` @@ -96,15 +107,69 @@ If the schedule will occur multiple times a day, specify the hourly recurrence. - Required: No - Type: object -- Default: `{}` -### Parameter: `notificationSettingsStatus` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`minute`](#parameter-hourlyrecurrenceminute) | int | Minutes of the hour the schedule will run. | + +### Parameter: `hourlyRecurrence.minute` + +Minutes of the hour the schedule will run. + +- Required: Yes +- Type: int + +### Parameter: `notificationSettings` + +The notification settings for the schedule. + +- Required: No +- Type: object + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailRecipient`](#parameter-notificationsettingsemailrecipient) | string | The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. | +| [`webHookUrl`](#parameter-notificationsettingswebhookurl) | string | The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`notificationLocale`](#parameter-notificationsettingsnotificationlocale) | string | The locale to use when sending a notification (fallback for unsupported languages is EN). | +| [`status`](#parameter-notificationsettingsstatus) | string | If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. | +| [`timeInMinutes`](#parameter-notificationsettingstimeinminutes) | int | Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. | + +### Parameter: `notificationSettings.emailRecipient` + +The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationSettings.webHookUrl` + +The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty. + +- Required: No +- Type: string + +### Parameter: `notificationSettings.notificationLocale` + +The locale to use when sending a notification (fallback for unsupported languages is EN). + +- Required: No +- Type: string + +### Parameter: `notificationSettings.status` -If notifications are enabled for this schedule (i.e. Enabled, Disabled). +If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled. - Required: No - Type: string -- Default: `'Disabled'` - Allowed: ```Bicep [ @@ -113,13 +178,12 @@ If notifications are enabled for this schedule (i.e. Enabled, Disabled). ] ``` -### Parameter: `notificationSettingsTimeInMinutes` +### Parameter: `notificationSettings.timeInMinutes` -Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes. +Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified. - Required: No - Type: int -- Default: `30` ### Parameter: `status` @@ -149,7 +213,6 @@ The resource ID to which the schedule belongs. - Required: No - Type: string -- Default: `''` ### Parameter: `timeZoneId` @@ -165,7 +228,27 @@ If the schedule will occur only some days of the week, specify the weekly recurr - Required: No - Type: object -- Default: `{}` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`time`](#parameter-weeklyrecurrencetime) | string | The time of day the schedule will occur. | +| [`weekdays`](#parameter-weeklyrecurrenceweekdays) | array | The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). | + +### Parameter: `weeklyRecurrence.time` + +The time of day the schedule will occur. + +- Required: Yes +- Type: string + +### Parameter: `weeklyRecurrence.weekdays` + +The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.). + +- Required: Yes +- Type: array ## Outputs diff --git a/avm/res/dev-test-lab/lab/schedule/main.bicep b/avm/res/dev-test-lab/lab/schedule/main.bicep index c71be08e40..fea3cc182f 100644 --- a/avm/res/dev-test-lab/lab/schedule/main.bicep +++ b/avm/res/dev-test-lab/lab/schedule/main.bicep @@ -25,13 +25,13 @@ param taskType string param tags object? @sys.description('Optional. If the schedule will occur once each day of the week, specify the daily recurrence.') -param dailyRecurrence object = {} +param dailyRecurrence dailyRecurrenceType @sys.description('Optional. If the schedule will occur multiple times a day, specify the hourly recurrence.') -param hourlyRecurrence object = {} +param hourlyRecurrence hourlyRecurrenceType @sys.description('Optional. If the schedule will occur only some days of the week, specify the weekly recurrence.') -param weeklyRecurrence object = {} +param weeklyRecurrence weeklyRecurrenceType @allowed([ 'Enabled' @@ -41,20 +41,13 @@ param weeklyRecurrence object = {} param status string = 'Enabled' @sys.description('Optional. The resource ID to which the schedule belongs.') -param targetResourceId string = '' +param targetResourceId string? @sys.description('Optional. The time zone ID (e.g. Pacific Standard time).') param timeZoneId string = 'Pacific Standard time' -@allowed([ - 'Enabled' - 'Disabled' -]) -@sys.description('Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled).') -param notificationSettingsStatus string = 'Disabled' - -@sys.description('Optional. Time in minutes before event at which notification will be sent. Optional if "notificationSettingsStatus" is set to "Enabled". Default is 30 minutes.') -param notificationSettingsTimeInMinutes int = 30 +@sys.description('Optional. The notification settings for the schedule.') +param notificationSettings notificationSettingsType resource lab 'Microsoft.DevTestLab/labs@2018-09-15' existing = { name: labName @@ -66,18 +59,13 @@ resource schedule 'Microsoft.DevTestLab/labs/schedules@2018-09-15' = { tags: tags properties: { taskType: taskType - dailyRecurrence: !empty(dailyRecurrence) ? dailyRecurrence : null - hourlyRecurrence: !empty(hourlyRecurrence) ? hourlyRecurrence : null - weeklyRecurrence: !empty(weeklyRecurrence) ? weeklyRecurrence : null + dailyRecurrence: dailyRecurrence + hourlyRecurrence: hourlyRecurrence + weeklyRecurrence: weeklyRecurrence status: status - targetResourceId: !empty(targetResourceId) ? targetResourceId : null + targetResourceId: targetResourceId timeZoneId: timeZoneId - notificationSettings: notificationSettingsStatus == 'Enabled' - ? { - status: notificationSettingsStatus - timeInMinutes: notificationSettingsTimeInMinutes - } - : {} + notificationSettings: notificationSettings } } @@ -89,3 +77,46 @@ output resourceId string = schedule.id @sys.description('The name of the resource group the schedule was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +type dailyRecurrenceType = { + @sys.description('Required. The time of day the schedule will occur.') + time: string +}? + +@export() +type hourlyRecurrenceType = { + @sys.description('Required. Minutes of the hour the schedule will run.') + minute: int +}? + +@export() +type weeklyRecurrenceType = { + @sys.description('Required. The time of day the schedule will occur.') + time: string + + @sys.description('Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.).') + weekdays: string[] +}? + +@export() +type notificationSettingsType = { + @description('Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if "webHookUrl" is empty.') + emailRecipient: string? + + @description('Optional. The locale to use when sending a notification (fallback for unsupported languages is EN).') + notificationLocale: string? + + @sys.description('Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled.') + status: 'Disabled' | 'Enabled'? + + @sys.description('Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified.') + timeInMinutes: int? + + @description('Conditional. The webhook URL to which the notification will be sent. Required if "emailRecipient" is empty.') + webHookUrl: string? +}? diff --git a/avm/res/dev-test-lab/lab/schedule/main.json b/avm/res/dev-test-lab/lab/schedule/main.json index be1257f81e..2b95e09f31 100644 --- a/avm/res/dev-test-lab/lab/schedule/main.json +++ b/avm/res/dev-test-lab/lab/schedule/main.json @@ -5,13 +5,117 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5684619798703415484" + "version": "0.29.47.4906", + "templateHash": "9010276477624635732" }, "name": "DevTest Lab Schedules", - "description": "This module deploys a DevTest Lab Schedule.\n\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", + "description": "This module deploys a DevTest Lab Schedule.\r\n\r\nLab schedules are used to modify the settings for auto-shutdown, auto-start for lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "dailyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "hourlyRecurrenceType": { + "type": "object", + "properties": { + "minute": { + "type": "int", + "metadata": { + "description": "Required. Minutes of the hour the schedule will run." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "weeklyRecurrenceType": { + "type": "object", + "properties": { + "time": { + "type": "string", + "metadata": { + "description": "Required. The time of day the schedule will occur." + } + }, + "weekdays": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The days of the week for which the schedule is set (e.g. Sunday, Monday, Tuesday, etc.)." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + }, + "notificationSettingsType": { + "type": "object", + "properties": { + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The email recipient to send notifications to (can be a list of semi-colon separated email addresses). Required if \"webHookUrl\" is empty." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled). Default is Disabled." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Time in minutes before event at which notification will be sent. Default is 30 minutes if status is Enabled and not specified." + } + }, + "webHookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The webhook URL to which the notification will be sent. Required if \"emailRecipient\" is empty." + } + } + }, + "nullable": true, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", @@ -47,22 +151,19 @@ } }, "dailyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/dailyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur once each day of the week, specify the daily recurrence." } }, "hourlyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/hourlyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur multiple times a day, specify the hourly recurrence." } }, "weeklyRecurrence": { - "type": "object", - "defaultValue": {}, + "$ref": "#/definitions/weeklyRecurrenceType", "metadata": { "description": "Optional. If the schedule will occur only some days of the week, specify the weekly recurrence." } @@ -80,7 +181,7 @@ }, "targetResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID to which the schedule belongs." } @@ -92,22 +193,10 @@ "description": "Optional. The time zone ID (e.g. Pacific Standard time)." } }, - "notificationSettingsStatus": { - "type": "string", - "defaultValue": "Disabled", - "allowedValues": [ - "Enabled", - "Disabled" - ], - "metadata": { - "description": "Optional. If notifications are enabled for this schedule (i.e. Enabled, Disabled)." - } - }, - "notificationSettingsTimeInMinutes": { - "type": "int", - "defaultValue": 30, + "notificationSettings": { + "$ref": "#/definitions/notificationSettingsType", "metadata": { - "description": "Optional. Time in minutes before event at which notification will be sent. Optional if \"notificationSettingsStatus\" is set to \"Enabled\". Default is 30 minutes." + "description": "Optional. The notification settings for the schedule." } } }, @@ -125,13 +214,13 @@ "tags": "[parameters('tags')]", "properties": { "taskType": "[parameters('taskType')]", - "dailyRecurrence": "[if(not(empty(parameters('dailyRecurrence'))), parameters('dailyRecurrence'), null())]", - "hourlyRecurrence": "[if(not(empty(parameters('hourlyRecurrence'))), parameters('hourlyRecurrence'), null())]", - "weeklyRecurrence": "[if(not(empty(parameters('weeklyRecurrence'))), parameters('weeklyRecurrence'), null())]", + "dailyRecurrence": "[parameters('dailyRecurrence')]", + "hourlyRecurrence": "[parameters('hourlyRecurrence')]", + "weeklyRecurrence": "[parameters('weeklyRecurrence')]", "status": "[parameters('status')]", - "targetResourceId": "[if(not(empty(parameters('targetResourceId'))), parameters('targetResourceId'), null())]", + "targetResourceId": "[parameters('targetResourceId')]", "timeZoneId": "[parameters('timeZoneId')]", - "notificationSettings": "[if(equals(parameters('notificationSettingsStatus'), 'Enabled'), createObject('status', parameters('notificationSettingsStatus'), 'timeInMinutes', parameters('notificationSettingsTimeInMinutes')), createObject())]" + "notificationSettings": "[parameters('notificationSettings')]" }, "dependsOn": [ "lab" diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep index 898647489d..65d06f3e05 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep @@ -66,11 +66,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'b08c589c-2c79-41bd-8195-d5e62ad12f67' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -224,8 +226,10 @@ module testDeployment '../../../main.bicep' = [ dailyRecurrence: { time: '0000' } - notificationSettingsStatus: 'Enabled' - notificationSettingsTimeInMinutes: 30 + notificationSettings: { + status: 'Enabled' + timeInMinutes: 30 + } } { name: 'LabVmAutoStart' @@ -249,9 +253,7 @@ module testDeployment '../../../main.bicep' = [ name: 'autoShutdown' description: 'Integration configured for auto-shutdown' events: [ - { - eventName: 'AutoShutdown' - } + 'AutoShutdown' ] emailRecipient: 'mail@contosodtlmail.com' webHookUrl: 'https://webhook.contosotest.com' @@ -260,9 +262,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'costThreshold' events: [ - { - eventName: 'Cost' - } + 'Cost' ] webHookUrl: 'https://webhook.contosotest.com' } @@ -271,10 +271,9 @@ module testDeployment '../../../main.bicep' = [ { name: 'Public Repo' displayName: 'Public Artifact Repo' - status: 'Disabled' + status: 'Enabled' uri: 'https://github.com/Azure/azure-devtestlab.git' sourceType: 'GitHub' - branchRef: 'master' folderPath: '/Artifacts' } { @@ -285,12 +284,28 @@ module testDeployment '../../../main.bicep' = [ sourceType: 'GitHub' branchRef: 'master' armTemplateFolderPath: '/Environments' + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'DevTest Lab' + labName: '${namePrefix}${serviceShort}001' + } + } + { + name: 'Private Repo' + displayName: 'Private Artifact Repo' + status: 'Disabled' + uri: 'https://github.com/Azure/azure-devtestlab.git' + folderPath: '/Artifacts' + armTemplateFolderPath: '/ArmTemplates' + branchRef: 'main' + securityToken: guid(baseTime) } ] costs: { status: 'Enabled' cycleType: 'CalendarMonth' target: 450 + currencyCode: 'AUD' thresholdValue100DisplayOnChart: 'Enabled' thresholdValue100SendNotificationWhenExceeded: 'Enabled' } diff --git a/avm/res/dev-test-lab/lab/version.json b/avm/res/dev-test-lab/lab/version.json index 83083db694..b3d560b1ad 100644 --- a/avm/res/dev-test-lab/lab/version.json +++ b/avm/res/dev-test-lab/lab/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.3", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep index b73bab89b6..07e8f88432 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep @@ -49,3 +49,49 @@ output resourceId string = virtualNetwork.id @sys.description('The name of the resource group the lab virtual network was created in.') output resourceGroupName string = resourceGroup().name + +// =============== // +// Definitions // +// =============== // + +@export() +type allowedSubnetType = { + @sys.description('Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)).') + allowPublicIp: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Required. The resource ID of the allowed subnet.') + resourceId: string + + @sys.description('Required. The name of the subnet as seen in the lab.') + labSubnetName: string +} + +@export() +type subnetOverrideType = { + @sys.description('Required. The name given to the subnet within the lab.') + labSubnetName: string + + @sys.description('Required. The resource ID of the subnet.') + resourceId: string + + @sys.description('Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)).') + sharedPublicIpAddressConfiguration: { + @sys.description('Required. Backend ports that virtual machines on this subnet are allowed to expose.') + allowedPorts: { + @sys.description('Required. Backend port of the target virtual machine.') + backendPort: int + + @sys.description('Required. Protocol type of the port.') + transportProtocol: 'Tcp' | 'Udp' + }[] + } + + @sys.description('Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny).') + useInVmCreationPermission: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny).') + usePublicIpAddressPermission: 'Allow' | 'Deny' | 'Default'? + + @sys.description('Optional. The virtual network pool associated with this subnet.') + virtualNetworkPoolName: string? +} diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.json b/avm/res/dev-test-lab/lab/virtualnetwork/main.json index 7e9745af75..1eab7d68eb 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.json +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.json @@ -5,13 +5,133 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6899804233871456970" + "version": "0.29.47.4906", + "templateHash": "18309816581107210302" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, + "definitions": { + "allowedSubnetType": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "subnetOverrideType": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } + } + } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." + } + } + }, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "labName": { "type": "string", diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md index 9dff45e745..190e267c09 100644 --- a/avm/res/document-db/database-account/README.md +++ b/avm/res/document-db/database-account/README.md @@ -27,9 +27,9 @@ This module deploys a DocumentDB Database Account. | `Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleAssignments) | | `Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions` | [2023-04-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2023-04-15/databaseAccounts/sqlRoleDefinitions) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -342,6 +342,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -480,6 +481,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -522,9 +524,16 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: name: 'kv-ref' // Non-required parameters location: '' - secretsKeyVault: { - keyVaultName: '' - primaryReadonlyConnectionStringSecretName: 'custom-secret-name' + secretsExportConfiguration: { + keyVaultResourceId: '' + primaryReadonlyConnectionStringSecretName: 'primaryReadonlyConnectionString' + primaryReadOnlyKeySecretName: 'primaryReadOnlyKey' + primaryWriteConnectionStringSecretName: 'primaryWriteConnectionString' + primaryWriteKeySecretName: 'primaryWriteKey' + secondaryReadonlyConnectionStringSecretName: 'secondaryReadonlyConnectionString' + secondaryReadonlyKeySecretName: 'secondaryReadonlyKey' + secondaryWriteConnectionStringSecretName: 'secondaryWriteConnectionString' + secondaryWriteKeySecretName: 'secondaryWriteKey' } } } @@ -550,10 +559,17 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: "location": { "value": "" }, - "secretsKeyVault": { + "secretsExportConfiguration": { "value": { - "keyVaultName": "", - "primaryReadonlyConnectionStringSecretName": "custom-secret-name" + "keyVaultResourceId": "", + "primaryReadonlyConnectionStringSecretName": "primaryReadonlyConnectionString", + "primaryReadOnlyKeySecretName": "primaryReadOnlyKey", + "primaryWriteConnectionStringSecretName": "primaryWriteConnectionString", + "primaryWriteKeySecretName": "primaryWriteKey", + "secondaryReadonlyConnectionStringSecretName": "secondaryReadonlyConnectionString", + "secondaryReadonlyKeySecretName": "secondaryReadonlyKey", + "secondaryWriteConnectionStringSecretName": "secondaryWriteConnectionString", + "secondaryWriteKeySecretName": "secondaryWriteKey" } } } @@ -593,6 +609,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -643,6 +660,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -900,6 +918,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1170,6 +1189,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -2159,9 +2179,13 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'Sql' subnetResourceId: '' tags: { @@ -2236,9 +2260,13 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "Sql", "subnetResourceId": "", "tags": { @@ -2292,7 +2320,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: | Parameter | Type | Description | | :-- | :-- | :-- | -| [`automaticFailover`](#parameter-automaticfailover) | bool | Default to true. Enable automatic failover for regions. | +| [`automaticFailover`](#parameter-automaticfailover) | bool | Enable automatic failover for regions. | | [`backupIntervalInMinutes`](#parameter-backupintervalinminutes) | int | Default to 240. An integer representing the interval in minutes between two backups. Only applies to periodic backup type. | | [`backupPolicyContinuousTier`](#parameter-backuppolicycontinuoustier) | string | Default to Continuous30Days. Configuration values for continuous mode backup. | | [`backupPolicyType`](#parameter-backuppolicytype) | string | Default to Continuous. Describes the mode of backups. Periodic backup must be used if multiple write locations are used. | @@ -2302,11 +2330,11 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: | [`databaseAccountOfferType`](#parameter-databaseaccountoffertype) | string | Default to Standard. The offer type for the Cosmos DB database account. | | [`defaultConsistencyLevel`](#parameter-defaultconsistencylevel) | string | Default to Session. The default consistency level of the Cosmos DB account. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | -| [`disableKeyBasedMetadataWriteAccess`](#parameter-disablekeybasedmetadatawriteaccess) | bool | Default to false. Disable write operations on metadata resources (databases, containers, throughput) via account keys. | -| [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Default to false. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication. | -| [`enableAnalyticalStorage`](#parameter-enableanalyticalstorage) | bool | Default to false. Flag to indicate whether to enable storage analytics. | -| [`enableFreeTier`](#parameter-enablefreetier) | bool | Default to false. Flag to indicate whether Free Tier is enabled. | -| [`enableMultipleWriteLocations`](#parameter-enablemultiplewritelocations) | bool | Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled. | +| [`disableKeyBasedMetadataWriteAccess`](#parameter-disablekeybasedmetadatawriteaccess) | bool | Disable write operations on metadata resources (databases, containers, throughput) via account keys. | +| [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication. | +| [`enableAnalyticalStorage`](#parameter-enableanalyticalstorage) | bool | Flag to indicate whether to enable storage analytics. | +| [`enableFreeTier`](#parameter-enablefreetier) | bool | Flag to indicate whether Free Tier is enabled. | +| [`enableMultipleWriteLocations`](#parameter-enablemultiplewritelocations) | bool | Enables the account to write in multiple locations. Periodic backup must be used if enabled. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`gremlinDatabases`](#parameter-gremlindatabases) | array | Gremlin Databases configurations. | | [`location`](#parameter-location) | string | Default to current resource group scope location. Location for all resources. | @@ -2319,7 +2347,7 @@ module databaseAccount 'br/public:avm/res/document-db/database-account: | [`networkRestrictions`](#parameter-networkrestrictions) | object | The network configuration of this module. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalIds' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | -| [`secretsKeyVault`](#parameter-secretskeyvault) | object | Key vault reference and secret settings to add the connection strings and keys generated by the cosmosdb account. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | | [`serverVersion`](#parameter-serverversion) | string | Default to 4.2. Specifies the MongoDB server version to use. | | [`sqlDatabases`](#parameter-sqldatabases) | array | SQL Databases configurations. | | [`sqlRoleAssignmentsPrincipalIds`](#parameter-sqlroleassignmentsprincipalids) | array | SQL Role Definitions configurations. | @@ -2335,7 +2363,7 @@ Name of the Database Account. ### Parameter: `automaticFailover` -Default to true. Enable automatic failover for regions. +Enable automatic failover for regions. - Required: No - Type: bool @@ -2602,7 +2630,7 @@ Resource ID of the diagnostic log analytics workspace. For security reasons, it ### Parameter: `disableKeyBasedMetadataWriteAccess` -Default to false. Disable write operations on metadata resources (databases, containers, throughput) via account keys. +Disable write operations on metadata resources (databases, containers, throughput) via account keys. - Required: No - Type: bool @@ -2610,15 +2638,15 @@ Default to false. Disable write operations on metadata resources (databases, con ### Parameter: `disableLocalAuth` -Default to false. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication. +Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication. - Required: No - Type: bool -- Default: `False` +- Default: `True` ### Parameter: `enableAnalyticalStorage` -Default to false. Flag to indicate whether to enable storage analytics. +Flag to indicate whether to enable storage analytics. - Required: No - Type: bool @@ -2626,7 +2654,7 @@ Default to false. Flag to indicate whether to enable storage analytics. ### Parameter: `enableFreeTier` -Default to false. Flag to indicate whether Free Tier is enabled. +Flag to indicate whether Free Tier is enabled. - Required: No - Type: bool @@ -2634,7 +2662,7 @@ Default to false. Flag to indicate whether Free Tier is enabled. ### Parameter: `enableMultipleWriteLocations` -Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled. +Enables the account to write in multiple locations. Periodic backup must be used if enabled. - Required: No - Type: bool @@ -2893,8 +2921,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -3084,19 +3111,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -3134,6 +3206,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -3184,6 +3257,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -3230,6 +3310,7 @@ Array of role assignment objects that contain the 'roleDefinitionIdOrName' and ' | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -3280,6 +3361,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -3297,9 +3385,9 @@ The principal type of the assigned principal ID. ] ``` -### Parameter: `secretsKeyVault` +### Parameter: `secretsExportConfiguration` -Key vault reference and secret settings to add the connection strings and keys generated by the cosmosdb account. +Key vault reference and secret settings for the module's secrets export. - Required: No - Type: object @@ -3308,88 +3396,80 @@ Key vault reference and secret settings to add the connection strings and keys g | Parameter | Type | Description | | :-- | :-- | :-- | -| [`keyVaultName`](#parameter-secretskeyvaultkeyvaultname) | string | The key vault name where to store the keys and connection strings generated by the modules. | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The resource ID of the key vault where to store the secrets of this module. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`primaryReadonlyConnectionStringSecretName`](#parameter-secretskeyvaultprimaryreadonlyconnectionstringsecretname) | string | Default to Primary-Readonly-ConnectionString. The primary readonly connection string secret name to create. | -| [`primaryReadOnlyKeySecretName`](#parameter-secretskeyvaultprimaryreadonlykeysecretname) | string | Default to Primary-Readonly-Key. The primary readonly key secret name to create. | -| [`primaryWriteConnectionStringSecretName`](#parameter-secretskeyvaultprimarywriteconnectionstringsecretname) | string | Default to Primary-Write-ConnectionString. The primary write connection string secret name to create. | -| [`primaryWriteKeySecretName`](#parameter-secretskeyvaultprimarywritekeysecretname) | string | Default to Primary-Write-Key. The primary write key secret name to create. | -| [`resourceGroupName`](#parameter-secretskeyvaultresourcegroupname) | string | Default to the resource group where this account is. The resource group name where the key vault is. | -| [`secondaryReadonlyConnectionStringSecretName`](#parameter-secretskeyvaultsecondaryreadonlyconnectionstringsecretname) | string | Default to Secondary-Readonly-ConnectionString. The primary readonly connection string secret name to create. | -| [`secondaryReadonlyKeySecretName`](#parameter-secretskeyvaultsecondaryreadonlykeysecretname) | string | Default to Secondary-Readonly-Key. The primary readonly key secret name to create. | -| [`secondaryWriteConnectionStringSecretName`](#parameter-secretskeyvaultsecondarywriteconnectionstringsecretname) | string | Default to Secondary-Write-ConnectionString. The primary write connection string secret name to create. | -| [`secondaryWriteKeySecretName`](#parameter-secretskeyvaultsecondarywritekeysecretname) | string | Default to Secondary-Write-Key. The primary write key secret name to create. | +| [`primaryReadonlyConnectionStringSecretName`](#parameter-secretsexportconfigurationprimaryreadonlyconnectionstringsecretname) | string | The primary readonly connection string secret name to create. | +| [`primaryReadOnlyKeySecretName`](#parameter-secretsexportconfigurationprimaryreadonlykeysecretname) | string | The primary readonly key secret name to create. | +| [`primaryWriteConnectionStringSecretName`](#parameter-secretsexportconfigurationprimarywriteconnectionstringsecretname) | string | The primary write connection string secret name to create. | +| [`primaryWriteKeySecretName`](#parameter-secretsexportconfigurationprimarywritekeysecretname) | string | The primary write key secret name to create. | +| [`secondaryReadonlyConnectionStringSecretName`](#parameter-secretsexportconfigurationsecondaryreadonlyconnectionstringsecretname) | string | The primary readonly connection string secret name to create. | +| [`secondaryReadonlyKeySecretName`](#parameter-secretsexportconfigurationsecondaryreadonlykeysecretname) | string | The primary readonly key secret name to create. | +| [`secondaryWriteConnectionStringSecretName`](#parameter-secretsexportconfigurationsecondarywriteconnectionstringsecretname) | string | The primary write connection string secret name to create. | +| [`secondaryWriteKeySecretName`](#parameter-secretsexportconfigurationsecondarywritekeysecretname) | string | The primary write key secret name to create. | -### Parameter: `secretsKeyVault.keyVaultName` +### Parameter: `secretsExportConfiguration.keyVaultResourceId` -The key vault name where to store the keys and connection strings generated by the modules. +The resource ID of the key vault where to store the secrets of this module. - Required: Yes - Type: string -### Parameter: `secretsKeyVault.primaryReadonlyConnectionStringSecretName` - -Default to Primary-Readonly-ConnectionString. The primary readonly connection string secret name to create. - -- Required: No -- Type: string - -### Parameter: `secretsKeyVault.primaryReadOnlyKeySecretName` +### Parameter: `secretsExportConfiguration.primaryReadonlyConnectionStringSecretName` -Default to Primary-Readonly-Key. The primary readonly key secret name to create. +The primary readonly connection string secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.primaryWriteConnectionStringSecretName` +### Parameter: `secretsExportConfiguration.primaryReadOnlyKeySecretName` -Default to Primary-Write-ConnectionString. The primary write connection string secret name to create. +The primary readonly key secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.primaryWriteKeySecretName` +### Parameter: `secretsExportConfiguration.primaryWriteConnectionStringSecretName` -Default to Primary-Write-Key. The primary write key secret name to create. +The primary write connection string secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.resourceGroupName` +### Parameter: `secretsExportConfiguration.primaryWriteKeySecretName` -Default to the resource group where this account is. The resource group name where the key vault is. +The primary write key secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.secondaryReadonlyConnectionStringSecretName` +### Parameter: `secretsExportConfiguration.secondaryReadonlyConnectionStringSecretName` -Default to Secondary-Readonly-ConnectionString. The primary readonly connection string secret name to create. +The primary readonly connection string secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.secondaryReadonlyKeySecretName` +### Parameter: `secretsExportConfiguration.secondaryReadonlyKeySecretName` -Default to Secondary-Readonly-Key. The primary readonly key secret name to create. +The primary readonly key secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.secondaryWriteConnectionStringSecretName` +### Parameter: `secretsExportConfiguration.secondaryWriteConnectionStringSecretName` -Default to Secondary-Write-ConnectionString. The primary write connection string secret name to create. +The primary write connection string secret name to create. - Required: No - Type: string -### Parameter: `secretsKeyVault.secondaryWriteKeySecretName` +### Parameter: `secretsExportConfiguration.secondaryWriteKeySecretName` -Default to Secondary-Write-Key. The primary write key secret name to create. +The primary write key secret name to create. - Required: No - Type: string @@ -3704,8 +3784,10 @@ Tags of the Database Account resource. | Output | Type | Description | | :-- | :-- | :-- | | `endpoint` | string | The endpoint of the database account. | +| `exportedSecrets` | | The references to the secrets exported to the provided Key Vault. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the database account. | +| `privateEndpoints` | array | The private endpoints of the database account. | | `resourceGroupName` | string | The name of the resource group the database account was created in. | | `resourceId` | string | The resource ID of the database account. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -3716,7 +3798,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/document-db/database-account/gremlin-database/graph/main.json b/avm/res/document-db/database-account/gremlin-database/graph/main.json index 2d4f0f12fa..13655f07f3 100644 --- a/avm/res/document-db/database-account/gremlin-database/graph/main.json +++ b/avm/res/document-db/database-account/gremlin-database/graph/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3987394297554402770" + "version": "0.29.47.4906", + "templateHash": "9917502444704809829" }, "name": "DocumentDB Database Accounts Gremlin Databases Graphs", "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph.", diff --git a/avm/res/document-db/database-account/gremlin-database/main.bicep b/avm/res/document-db/database-account/gremlin-database/main.bicep index 2163122222..c1665f5d92 100644 --- a/avm/res/document-db/database-account/gremlin-database/main.bicep +++ b/avm/res/document-db/database-account/gremlin-database/main.bicep @@ -54,7 +54,7 @@ module gremlinDatabase_gremlinGraphs 'graph/main.bicep' = [ name: graph.name gremlinDatabaseName: name databaseAccountName: databaseAccountName - indexingPolicy: contains(graph, 'indexingPolicy') ? graph.indexingPolicy : true + indexingPolicy: graph.?indexingPolicy partitionKeyPaths: !empty(graph.partitionKeyPaths) ? graph.partitionKeyPaths : [] } } diff --git a/avm/res/document-db/database-account/gremlin-database/main.json b/avm/res/document-db/database-account/gremlin-database/main.json index 57548e1867..4f38214e0d 100644 --- a/avm/res/document-db/database-account/gremlin-database/main.json +++ b/avm/res/document-db/database-account/gremlin-database/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6889435067791947905" + "version": "0.29.47.4906", + "templateHash": "7926803681315745584" }, "name": "DocumentDB Database Account Gremlin Databases", "description": "This module deploys a Gremlin Database within a CosmosDB Account.", @@ -99,7 +99,9 @@ "databaseAccountName": { "value": "[parameters('databaseAccountName')]" }, - "indexingPolicy": "[if(contains(parameters('graphs')[copyIndex()], 'indexingPolicy'), createObject('value', parameters('graphs')[copyIndex()].indexingPolicy), createObject('value', true()))]", + "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": { @@ -109,8 +111,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3987394297554402770" + "version": "0.29.47.4906", + "templateHash": "9917502444704809829" }, "name": "DocumentDB Database Accounts Gremlin Databases Graphs", "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph.", diff --git a/avm/res/document-db/database-account/main.bicep b/avm/res/document-db/database-account/main.bicep index b46ca70841..bde70c52ef 100644 --- a/avm/res/document-db/database-account/main.bicep +++ b/avm/res/document-db/database-account/main.bicep @@ -33,22 +33,22 @@ param locations failoverLocationsType[] = [] @description('Optional. Default to Session. The default consistency level of the Cosmos DB account.') param defaultConsistencyLevel string = 'Session' -@description('Optional. Default to false. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication.') -param disableLocalAuth bool = false +@description('Optional. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication.') +param disableLocalAuth bool = true -@description('Optional. Default to false. Flag to indicate whether to enable storage analytics.') +@description('Optional. Flag to indicate whether to enable storage analytics.') param enableAnalyticalStorage bool = false -@description('Optional. Default to true. Enable automatic failover for regions.') +@description('Optional. Enable automatic failover for regions.') param automaticFailover bool = true -@description('Optional. Default to false. Flag to indicate whether Free Tier is enabled.') +@description('Optional. Flag to indicate whether Free Tier is enabled.') param enableFreeTier bool = false -@description('Optional. Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled.') +@description('Optional. Enables the account to write in multiple locations. Periodic backup must be used if enabled.') param enableMultipleWriteLocations bool = false -@description('Optional. Default to false. Disable write operations on metadata resources (databases, containers, throughput) via account keys.') +@description('Optional. Disable write operations on metadata resources (databases, containers, throughput) via account keys.') param disableKeyBasedMetadataWriteAccess bool = false @minValue(1) @@ -143,8 +143,8 @@ param backupStorageRedundancy string = 'Local' @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType -@description('Optional. Key vault reference and secret settings to add the connection strings and keys generated by the cosmosdb account.') -param secretsKeyVault secretsKeyVaultType? +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? @description('Optional. The network configuration of this module.') param networkRestrictions networkRestrictionsType? @@ -242,6 +242,7 @@ var virtualNetworkRules = [ var databaseAccountProperties = union( { databaseAccountOfferType: databaseAccountOfferType + backupPolicy: backupPolicy }, ((!empty(sqlDatabases) || !empty(mongodbDatabases) || !empty(gremlinDatabases)) ? { @@ -258,7 +259,6 @@ var databaseAccountProperties = union( capabilities: capabilities enableFreeTier: enableFreeTier - backupPolicy: backupPolicy enableAutomaticFailover: automaticFailover enableAnalyticalStorage: enableAnalyticalStorage } @@ -314,6 +314,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.documentdb-databaseaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -383,14 +394,10 @@ resource databaseAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettin ] resource databaseAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(databaseAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(databaseAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -436,7 +443,7 @@ module databaseAccount_mongodbDatabases 'mongodb-database/main.bicep' = [ databaseAccountName: databaseAccount.name name: mongodbDatabase.name tags: mongodbDatabase.?tags ?? tags - collections: contains(mongodbDatabase, 'collections') ? mongodbDatabase.collections : [] + collections: mongodbDatabase.?collections throughput: mongodbDatabase.?throughput } } @@ -449,14 +456,14 @@ module databaseAccount_gremlinDatabases 'gremlin-database/main.bicep' = [ databaseAccountName: databaseAccount.name name: gremlinDatabase.name tags: gremlinDatabase.?tags ?? tags - graphs: contains(gremlinDatabase, 'graphs') ? gremlinDatabase.graphs : [] - maxThroughput: contains(gremlinDatabase, 'maxThroughput') ? gremlinDatabase.maxThroughput : 4000 + graphs: gremlinDatabase.?graphs + maxThroughput: gremlinDatabase.?maxThroughput throughput: gremlinDatabase.?throughput } } ] -module databaseAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module databaseAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-databaseAccount-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -497,8 +504,7 @@ module databaseAccount_privateEndpoints 'br/public:avm/res/network/private-endpo 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -509,49 +515,89 @@ module databaseAccount_privateEndpoints 'br/public:avm/res/network/private-endpo } ] -module keyVault 'modules/secrets-key-vault.bicep' = if (secretsKeyVault != null) { +module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) { name: '${uniqueString(deployment().name, location)}-secrets-kv' - scope: resourceGroup(secretsKeyVault.?resourceGroupName ?? resourceGroup().name) + scope: resourceGroup( + split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2], + split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4] + ) params: { - keyVaultName: secretsKeyVault!.keyVaultName - - keySecrets: [ - { - secretName: secretsKeyVault.?primaryWriteKeySecretName ?? 'Primary-Write-Key' - secretValue: databaseAccount.listKeys().primaryMasterKey - } - { - secretName: secretsKeyVault.?primaryReadOnlyKeySecretName ?? 'Primary-Readonly-Key' - secretValue: databaseAccount.listKeys().primaryReadonlyMasterKey - } - { - secretName: secretsKeyVault.?primaryWriteConnectionStringSecretName ?? 'Primary-Write-ConnectionString' - secretValue: databaseAccount.listConnectionStrings().connectionStrings[0].connectionString - } - { - secretName: secretsKeyVault.?primaryReadonlyConnectionStringSecretName ?? 'Primary-Readonly-ConnectionString' - secretValue: databaseAccount.listConnectionStrings().connectionStrings[2].connectionString - } - { - secretName: secretsKeyVault.?secondaryWriteKeySecretName ?? 'Secondary-Write-Key' - secretValue: databaseAccount.listKeys().secondaryMasterKey - } - { - secretName: secretsKeyVault.?secondaryReadonlyKeySecretName ?? 'Secondary-Readonly-Key' - secretValue: databaseAccount.listKeys().secondaryReadonlyMasterKey - } - { - secretName: secretsKeyVault.?secondaryWriteConnectionStringSecretName ?? 'Secondary-Write-ConnectionString' - secretValue: databaseAccount.listConnectionStrings().connectionStrings[1].connectionString - } - { - secretName: secretsKeyVault.?secondaryReadonlyConnectionStringSecretName ?? 'Secondary-Readonly-ConnectionString' - secretValue: databaseAccount.listConnectionStrings().connectionStrings[3].connectionString - } - ] + keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/')) + secretsToSet: union( + [], + contains(secretsExportConfiguration!, 'primaryWriteKeySecretName') + ? [ + { + name: secretsExportConfiguration!.primaryWriteKeySecretName + value: databaseAccount.listKeys().primaryMasterKey + } + ] + : [], + contains(secretsExportConfiguration!, 'primaryReadOnlyKeySecretName') + ? [ + { + name: secretsExportConfiguration!.primaryReadOnlyKeySecretName + value: databaseAccount.listKeys().primaryReadonlyMasterKey + } + ] + : [], + contains(secretsExportConfiguration!, 'primaryWriteConnectionStringSecretName') + ? [ + { + name: secretsExportConfiguration!.primaryWriteConnectionStringSecretName + value: databaseAccount.listConnectionStrings().connectionStrings[0].connectionString + } + ] + : [], + contains(secretsExportConfiguration!, 'primaryReadonlyConnectionStringSecretName') + ? [ + { + name: secretsExportConfiguration!.primaryReadonlyConnectionStringSecretName + value: databaseAccount.listConnectionStrings().connectionStrings[2].connectionString + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryWriteKeySecretName') + ? [ + { + name: secretsExportConfiguration!.secondaryWriteKeySecretName + value: databaseAccount.listKeys().secondaryMasterKey + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryReadonlyKeySecretName') + ? [ + { + name: secretsExportConfiguration!.secondaryReadonlyKeySecretName + value: databaseAccount.listKeys().secondaryReadonlyMasterKey + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryWriteConnectionStringSecretName') + ? [ + { + name: secretsExportConfiguration!.secondaryWriteConnectionStringSecretName + value: databaseAccount.listConnectionStrings().connectionStrings[1].connectionString + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryReadonlyConnectionStringSecretName') + ? [ + { + name: secretsExportConfiguration!.secondaryReadonlyConnectionStringSecretName + value: databaseAccount.listConnectionStrings().connectionStrings[3].connectionString + } + ] + : [] + ) } } +@description('The references to the secrets exported to the provided Key Vault.') +output exportedSecrets secretsOutputType = (secretsExportConfiguration != null) + ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret) + : {} + @description('The name of the database account.') output name string = databaseAccount.name @@ -570,6 +616,17 @@ output location string = databaseAccount.location @description('The endpoint of the database account.') output endpoint string = databaseAccount.properties.documentEndpoint +@description('The private endpoints of the database account.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: databaseAccount_privateEndpoints[i].outputs.name + resourceId: databaseAccount_privateEndpoints[i].outputs.resourceId + groupId: databaseAccount_privateEndpoints[i].outputs.groupId + customDnsConfig: databaseAccount_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: databaseAccount_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -591,6 +648,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -629,11 +689,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @@ -824,38 +893,41 @@ type sqlDatabaseType = { }[]? } -type secretsKeyVaultType = { - @description('Required. The key vault name where to store the keys and connection strings generated by the modules.') - keyVaultName: string - - @description('Optional. Default to the resource group where this account is. The resource group name where the key vault is.') - resourceGroupName: string? +type secretsExportConfigurationType = { + @description('Required. The resource ID of the key vault where to store the secrets of this module.') + keyVaultResourceId: string - @description('Optional. Default to Primary-Write-Key. The primary write key secret name to create.') + @description('Optional. The primary write key secret name to create.') primaryWriteKeySecretName: string? - @description('Optional. Default to Primary-Readonly-Key. The primary readonly key secret name to create.') + @description('Optional. The primary readonly key secret name to create.') primaryReadOnlyKeySecretName: string? - @description('Optional. Default to Primary-Write-ConnectionString. The primary write connection string secret name to create.') + @description('Optional. The primary write connection string secret name to create.') primaryWriteConnectionStringSecretName: string? - @description('Optional. Default to Primary-Readonly-ConnectionString. The primary readonly connection string secret name to create.') + @description('Optional. The primary readonly connection string secret name to create.') primaryReadonlyConnectionStringSecretName: string? - @description('Optional. Default to Secondary-Write-Key. The primary write key secret name to create.') + @description('Optional. The primary write key secret name to create.') secondaryWriteKeySecretName: string? - @description('Optional. Default to Secondary-Readonly-Key. The primary readonly key secret name to create.') + @description('Optional. The primary readonly key secret name to create.') secondaryReadonlyKeySecretName: string? - @description('Optional. Default to Secondary-Write-ConnectionString. The primary write connection string secret name to create.') + @description('Optional. The primary write connection string secret name to create.') secondaryWriteConnectionStringSecretName: string? - @description('Optional. Default to Secondary-Readonly-ConnectionString. The primary readonly connection string secret name to create.') + @description('Optional. The primary readonly connection string secret name to create.') secondaryReadonlyConnectionStringSecretName: string? } +import { secretSetType } from 'modules/keyVaultExport.bicep' +type secretsOutputType = { + @description('An exported secret\'s references.') + *: secretSetType +} + type networkRestrictionsType = { @description('Optional. Default to []. A single IPv4 address or a single IPv4 address range in CIDR format. Provided IPs must be well-formatted and cannot be contained in one of the following ranges: 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16, since these are not enforceable by the IP address filter. Example of valid inputs: "23.40.210.245" or "23.40.210.0/8".') ipRules: string[] diff --git a/avm/res/document-db/database-account/main.json b/avm/res/document-db/database-account/main.json index 73128edf65..b09e8f067f 100644 --- a/avm/res/document-db/database-account/main.json +++ b/avm/res/document-db/database-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12689851031952872564" + "version": "0.29.47.4906", + "templateHash": "17113672202913052529" }, "name": "DocumentDB Database Accounts", "description": "This module deploys a DocumentDB Database Account.", @@ -66,6 +66,13 @@ "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": { @@ -165,21 +172,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -675,80 +705,83 @@ } } }, - "secretsKeyVaultType": { + "secretsExportConfigurationType": { "type": "object", "properties": { - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." - } - }, - "resourceGroupName": { + "keyVaultResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. Default to the resource group where this account is. The resource group name where the key vault is." + "description": "Required. The resource ID of the key vault where to store the secrets of this module." } }, "primaryWriteKeySecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Primary-Write-Key. The primary write key secret name to create." + "description": "Optional. The primary write key secret name to create." } }, "primaryReadOnlyKeySecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Primary-Readonly-Key. The primary readonly key secret name to create." + "description": "Optional. The primary readonly key secret name to create." } }, "primaryWriteConnectionStringSecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Primary-Write-ConnectionString. The primary write connection string secret name to create." + "description": "Optional. The primary write connection string secret name to create." } }, "primaryReadonlyConnectionStringSecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Primary-Readonly-ConnectionString. The primary readonly connection string secret name to create." + "description": "Optional. The primary readonly connection string secret name to create." } }, "secondaryWriteKeySecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Secondary-Write-Key. The primary write key secret name to create." + "description": "Optional. The primary write key secret name to create." } }, "secondaryReadonlyKeySecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Secondary-Readonly-Key. The primary readonly key secret name to create." + "description": "Optional. The primary readonly key secret name to create." } }, "secondaryWriteConnectionStringSecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Secondary-Write-ConnectionString. The primary write connection string secret name to create." + "description": "Optional. The primary write connection string secret name to create." } }, "secondaryReadonlyConnectionStringSecretName": { "type": "string", "nullable": true, "metadata": { - "description": "Optional. Default to Secondary-Readonly-ConnectionString. The primary readonly connection string secret name to create." + "description": "Optional. The primary readonly connection string secret name to create." } } } }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, "networkRestrictionsType": { "type": "object", "properties": { @@ -801,6 +834,28 @@ } } } + }, + "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": { @@ -866,44 +921,44 @@ }, "disableLocalAuth": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { - "description": "Optional. Default to false. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication." + "description": "Optional. Opt-out of local authentication and ensure only MSI and AAD can be used exclusively for authentication." } }, "enableAnalyticalStorage": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Default to false. Flag to indicate whether to enable storage analytics." + "description": "Optional. Flag to indicate whether to enable storage analytics." } }, "automaticFailover": { "type": "bool", "defaultValue": true, "metadata": { - "description": "Optional. Default to true. Enable automatic failover for regions." + "description": "Optional. Enable automatic failover for regions." } }, "enableFreeTier": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Default to false. Flag to indicate whether Free Tier is enabled." + "description": "Optional. Flag to indicate whether Free Tier is enabled." } }, "enableMultipleWriteLocations": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Default to false. Enables the account to write in multiple locations. Periodic backup must be used if enabled." + "description": "Optional. Enables the account to write in multiple locations. Periodic backup must be used if enabled." } }, "disableKeyBasedMetadataWriteAccess": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Default to false. Disable write operations on metadata resources (databases, containers, throughput) via account keys." + "description": "Optional. Disable write operations on metadata resources (databases, containers, throughput) via account keys." } }, "maxStalenessPrefix": { @@ -1075,11 +1130,11 @@ "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." } }, - "secretsKeyVault": { - "$ref": "#/definitions/secretsKeyVaultType", + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", "nullable": true, "metadata": { - "description": "Optional. Key vault reference and secret settings to add the connection strings and keys generated by the cosmosdb account." + "description": "Optional. Key vault reference and secret settings for the module's secrets export." } }, "networkRestrictions": { @@ -1122,6 +1177,11 @@ "id": "[coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray())[copyIndex('virtualNetworkRules')].subnetResourceId]", "ignoreMissingVnetServiceEndpoint": false } + }, + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", @@ -1154,7 +1214,7 @@ ], "kind": "[if(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('gremlinDatabases')))), 'GlobalDocumentDB', if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB'))]", "backupPolicy": "[if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('type', parameters('backupPolicyType'), 'continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject('type', parameters('backupPolicyType'), 'periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))))]", - "databaseAccountProperties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType')), if(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'AzureServices'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Enabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'capabilities', variables('capabilities'), 'enableFreeTier', parameters('enableFreeTier'), 'backupPolicy', variables('backupPolicy'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(not(empty(parameters('sqlDatabases'))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]", + "databaseAccountProperties": "[union(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', variables('backupPolicy')), if(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), createObject('consistencyPolicy', variables('consistencyPolicy')[parameters('defaultConsistencyLevel')], 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(empty(variables('databaseAccount_locations')), variables('defaultFailoverLocation'), variables('databaseAccount_locations')), 'ipRules', variables('ipRules'), 'virtualNetworkRules', variables('virtualNetworkRules'), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'AzureServices'), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Enabled'), 'isVirtualNetworkFilterEnabled', or(not(empty(variables('ipRules'))), not(empty(variables('virtualNetworkRules')))), 'capabilities', variables('capabilities'), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(not(empty(parameters('sqlDatabases'))), createObject('disableLocalAuth', parameters('disableLocalAuth'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess')), createObject()), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject()))]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", @@ -1257,20 +1317,20 @@ "databaseAccount_roleAssignments": { "copy": { "name": "databaseAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1313,8 +1373,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5335306568168720143" + "version": "0.29.47.4906", + "templateHash": "17028659150619761460" }, "name": "DocumentDB Database Account SQL Databases", "description": "This module deploys a SQL Database in a CosmosDB Account.", @@ -1446,8 +1506,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18224989843504865412" + "version": "0.29.47.4906", + "templateHash": "15811275148784494613" }, "name": "DocumentDB Database Account SQL Database Containers", "description": "This module deploys a SQL Database Container in a CosmosDB Account.", @@ -1703,8 +1763,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6438933554145929110" + "version": "0.29.47.4906", + "templateHash": "8574173933379504173" }, "name": "DocumentDB Database Account SQL Role.", "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account.", @@ -1790,8 +1850,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5774063578247320117" + "version": "0.29.47.4906", + "templateHash": "13173648139881140212" }, "name": "DocumentDB Database Account SQL Role Definitions.", "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", @@ -1911,8 +1971,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4968352550056104037" + "version": "0.29.47.4906", + "templateHash": "16344872287220693060" }, "name": "DocumentDB Database Account SQL Role Assignments.", "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", @@ -2011,7 +2071,9 @@ "tags": { "value": "[coalesce(tryGet(parameters('mongodbDatabases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "collections": "[if(contains(parameters('mongodbDatabases')[copyIndex()], 'collections'), createObject('value', parameters('mongodbDatabases')[copyIndex()].collections), createObject('value', createArray()))]", + "collections": { + "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'collections')]" + }, "throughput": { "value": "[tryGet(parameters('mongodbDatabases')[copyIndex()], 'throughput')]" } @@ -2023,8 +2085,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10789567092566068185" + "version": "0.29.47.4906", + "templateHash": "5589296472144391886" }, "name": "DocumentDB Database Account MongoDB Databases", "description": "This module deploys a MongoDB Database within a CosmosDB Account.", @@ -2126,8 +2188,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15131365585122011750" + "version": "0.29.47.4906", + "templateHash": "2005645426653376123" }, "name": "DocumentDB Database Account MongoDB Database Collections", "description": "This module deploys a MongoDB Database Collection.", @@ -2269,8 +2331,12 @@ "tags": { "value": "[coalesce(tryGet(parameters('gremlinDatabases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "graphs": "[if(contains(parameters('gremlinDatabases')[copyIndex()], 'graphs'), createObject('value', parameters('gremlinDatabases')[copyIndex()].graphs), createObject('value', createArray()))]", - "maxThroughput": "[if(contains(parameters('gremlinDatabases')[copyIndex()], 'maxThroughput'), createObject('value', parameters('gremlinDatabases')[copyIndex()].maxThroughput), createObject('value', 4000))]", + "graphs": { + "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'graphs')]" + }, + "maxThroughput": { + "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'maxThroughput')]" + }, "throughput": { "value": "[tryGet(parameters('gremlinDatabases')[copyIndex()], 'throughput')]" } @@ -2282,8 +2348,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6730968766448186151" + "version": "0.29.47.4906", + "templateHash": "7926803681315745584" }, "name": "DocumentDB Database Account Gremlin Databases", "description": "This module deploys a Gremlin Database within a CosmosDB Account.", @@ -2376,7 +2442,9 @@ "databaseAccountName": { "value": "[parameters('databaseAccountName')]" }, - "indexingPolicy": "[if(contains(parameters('graphs')[copyIndex()], 'indexingPolicy'), createObject('value', parameters('graphs')[copyIndex()].indexingPolicy), createObject('value', true()))]", + "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": { @@ -2386,8 +2454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16910156821054362754" + "version": "0.29.47.4906", + "templateHash": "9917502444704809829" }, "name": "DocumentDB Database Accounts Gremlin Databases Graphs", "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph.", @@ -2560,11 +2628,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2592,19 +2657,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2708,13 +2801,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2748,8 +2841,11 @@ "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." + "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": { @@ -2789,8 +2885,11 @@ "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." + "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": { @@ -2838,6 +2937,29 @@ } }, "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": { @@ -2873,18 +2995,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2940,6 +3055,13 @@ } }, "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')]", @@ -2957,8 +3079,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2976,7 +3098,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3017,27 +3139,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -3048,28 +3170,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -3077,12 +3223,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -3096,27 +3245,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -3174,14 +3332,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -3190,12 +3362,13 @@ "databaseAccount" ] }, - "keyVault": { - "condition": "[not(equals(parameters('secretsKeyVault'), null()))]", + "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')))]", - "resourceGroup": "[coalesce(tryGet(parameters('secretsKeyVault'), 'resourceGroupName'), resourceGroup().name)]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3203,43 +3376,10 @@ "mode": "Incremental", "parameters": { "keyVaultName": { - "value": "[parameters('secretsKeyVault').keyVaultName]" + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" }, - "keySecrets": { - "value": [ - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'primaryWriteKeySecretName'), 'Primary-Write-Key')]", - "secretValue": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryMasterKey]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'primaryReadOnlyKeySecretName'), 'Primary-Readonly-Key')]", - "secretValue": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryReadonlyMasterKey]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'primaryWriteConnectionStringSecretName'), 'Primary-Write-ConnectionString')]", - "secretValue": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[0].connectionString]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'primaryReadonlyConnectionStringSecretName'), 'Primary-Readonly-ConnectionString')]", - "secretValue": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[2].connectionString]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'secondaryWriteKeySecretName'), 'Secondary-Write-Key')]", - "secretValue": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryMasterKey]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'secondaryReadonlyKeySecretName'), 'Secondary-Readonly-Key')]", - "secretValue": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryReadonlyMasterKey]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'secondaryWriteConnectionStringSecretName'), 'Secondary-Write-ConnectionString')]", - "secretValue": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[1].connectionString]" - }, - { - "secretName": "[coalesce(tryGet(parameters('secretsKeyVault'), 'secondaryReadonlyConnectionStringSecretName'), 'Secondary-Readonly-ConnectionString')]", - "secretValue": "[listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[3].connectionString]" - } - ] + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryWriteKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadOnlyKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryReadOnlyKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').primaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryWriteConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryWriteConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[0].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'primaryReadonlyConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryReadonlyConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[2].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryWriteKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyKeySecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryReadonlyKeySecretName, 'value', listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').secondaryReadonlyMasterKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryWriteConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryWriteConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[1].connectionString)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryReadonlyConnectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryReadonlyConnectionStringSecretName, 'value', listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '2023-04-15').connectionStrings[3].connectionString)), createArray()))]" } }, "template": { @@ -3249,56 +3389,106 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13154453205210713878" + "version": "0.29.47.4906", + "templateHash": "986606208324987345" } }, "definitions": { - "keySecret": { + "secretSetType": { "type": "object", "properties": { - "secretName": { - "type": "string" + "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." + } }, - "secretValue": { - "type": "securestring" + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } } } } }, "parameters": { "keyVaultName": { - "type": "string" + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } }, - "keySecrets": { + "secretsToSet": { "type": "array", "items": { - "$ref": "#/definitions/keySecret" + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." } } }, "resources": { - "kv": { + "keyVault": { "existing": true, "type": "Microsoft.KeyVault/vaults", "apiVersion": "2022-07-01", "name": "[parameters('keyVaultName')]" }, - "keySecretsSecrets": { + "secrets": { "copy": { - "name": "keySecretsSecrets", - "count": "[length(parameters('keySecrets'))]" + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" }, "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('keySecrets')[copyIndex()].secretName)]", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", "properties": { - "value": "[parameters('keySecrets')[copyIndex()].secretValue]" + "value": "[parameters('secretsToSet')[copyIndex()].value]" }, "dependsOn": [ - "kv" + "keyVault" ] } + }, + "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]" + } + } + } } } }, @@ -3308,6 +3498,13 @@ } }, "outputs": { + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, "name": { "type": "string", "metadata": { @@ -3349,6 +3546,22 @@ "description": "The endpoint of the database account." }, "value": "[reference('databaseAccount').documentEndpoint]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the database account." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/document-db/database-account/modules/keyVaultExport.bicep b/avm/res/document-db/database-account/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..bc888ac9ed --- /dev/null +++ b/avm/res/document-db/database-account/modules/keyVaultExport.bicep @@ -0,0 +1,50 @@ +@description('Required. The name of the Key Vault to set the ecrets in.') +param keyVaultName string + +@description('Required. The secrets to set in the Key Vault.') +param secretsToSet secretToSetType[] + +resource keyVault 'Microsoft.KeyVault/vaults@2022-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 + } + } +] + +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetType[] = [ + #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 + } +] + +// =============== // +// Definitions // +// =============== // + +@export() +type secretSetType = { + @description('The resourceId of the exported secret.') + secretResourceId: string + + @description('The secret URI of the exported secret.') + secretUri: string +} + +type secretToSetType = { + @description('Required. The name of the secret to set.') + name: string + + @description('Required. The value of the secret to set.') + @secure() + value: string +} diff --git a/avm/res/document-db/database-account/modules/secrets-key-vault.bicep b/avm/res/document-db/database-account/modules/secrets-key-vault.bicep deleted file mode 100644 index 803afa1f72..0000000000 --- a/avm/res/document-db/database-account/modules/secrets-key-vault.bicep +++ /dev/null @@ -1,23 +0,0 @@ -param keyVaultName string -param keySecrets keySecret[] - -resource kv 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -resource keySecretsSecrets 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = [ - for secret in keySecrets: { - name: secret.secretName - parent: kv - properties: { - value: secret.secretValue - } - } -] - -type keySecret = { - secretName: string - - @secure() - secretValue: string -} diff --git a/avm/res/document-db/database-account/mongodb-database/collection/main.json b/avm/res/document-db/database-account/mongodb-database/collection/main.json index eeb2943ec8..7ab4316f20 100644 --- a/avm/res/document-db/database-account/mongodb-database/collection/main.json +++ b/avm/res/document-db/database-account/mongodb-database/collection/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6235322895830297683" + "version": "0.29.47.4906", + "templateHash": "2005645426653376123" }, "name": "DocumentDB Database Account MongoDB Database Collections", "description": "This module deploys a MongoDB Database Collection.", diff --git a/avm/res/document-db/database-account/mongodb-database/main.json b/avm/res/document-db/database-account/mongodb-database/main.json index a862d99d20..5f89eca19e 100644 --- a/avm/res/document-db/database-account/mongodb-database/main.json +++ b/avm/res/document-db/database-account/mongodb-database/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1527748615955553712" + "version": "0.29.47.4906", + "templateHash": "5589296472144391886" }, "name": "DocumentDB Database Account MongoDB Databases", "description": "This module deploys a MongoDB Database within a CosmosDB Account.", @@ -108,8 +108,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6235322895830297683" + "version": "0.29.47.4906", + "templateHash": "2005645426653376123" }, "name": "DocumentDB Database Account MongoDB Database Collections", "description": "This module deploys a MongoDB Database Collection.", diff --git a/avm/res/document-db/database-account/sql-database/container/main.json b/avm/res/document-db/database-account/sql-database/container/main.json index f882248bfc..2f20bcd666 100644 --- a/avm/res/document-db/database-account/sql-database/container/main.json +++ b/avm/res/document-db/database-account/sql-database/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3824205620211058652" + "version": "0.29.47.4906", + "templateHash": "15811275148784494613" }, "name": "DocumentDB Database Account SQL Database Containers", "description": "This module deploys a SQL Database Container in a CosmosDB Account.", diff --git a/avm/res/document-db/database-account/sql-database/main.json b/avm/res/document-db/database-account/sql-database/main.json index 3bce557b29..4bf3ca6f3c 100644 --- a/avm/res/document-db/database-account/sql-database/main.json +++ b/avm/res/document-db/database-account/sql-database/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "8319829662998768775" + "version": "0.29.47.4906", + "templateHash": "17028659150619761460" }, "name": "DocumentDB Database Account SQL Databases", "description": "This module deploys a SQL Database in a CosmosDB Account.", @@ -138,8 +138,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3824205620211058652" + "version": "0.29.47.4906", + "templateHash": "15811275148784494613" }, "name": "DocumentDB Database Account SQL Database Containers", "description": "This module deploys a SQL Database Container in a CosmosDB Account.", diff --git a/avm/res/document-db/database-account/sql-role/main.json b/avm/res/document-db/database-account/sql-role/main.json index 0d05737081..071740e45e 100644 --- a/avm/res/document-db/database-account/sql-role/main.json +++ b/avm/res/document-db/database-account/sql-role/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6438933554145929110" + "version": "0.29.47.4906", + "templateHash": "8574173933379504173" }, "name": "DocumentDB Database Account SQL Role.", "description": "This module deploys SQL Role Definision and Assignment in a CosmosDB Account.", @@ -91,8 +91,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5774063578247320117" + "version": "0.29.47.4906", + "templateHash": "13173648139881140212" }, "name": "DocumentDB Database Account SQL Role Definitions.", "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4968352550056104037" + "version": "0.29.47.4906", + "templateHash": "16344872287220693060" }, "name": "DocumentDB Database Account SQL Role Assignments.", "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", diff --git a/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json index 97c74bb0ed..59ecc153e1 100644 --- a/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json +++ b/avm/res/document-db/database-account/sql-role/sql-role-assignments/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12924779272926798782" + "version": "0.29.47.4906", + "templateHash": "16344872287220693060" }, "name": "DocumentDB Database Account SQL Role Assignments.", "description": "This module deploys a SQL Role Assignment in a CosmosDB Account.", diff --git a/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json index b7029bd4a1..2b240d1905 100644 --- a/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json +++ b/avm/res/document-db/database-account/sql-role/sql-role-definitions/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5774063578247320117" + "version": "0.29.47.4906", + "templateHash": "13173648139881140212" }, "name": "DocumentDB Database Account SQL Role Definitions.", "description": "This module deploys a SQL Role Definision in a CosmosDB Account.", diff --git a/avm/res/document-db/database-account/tests/e2e/gremlindb/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/gremlindb/main.test.bicep index 4b9652bdd6..2fc70eae4b 100644 --- a/avm/res/document-db/database-account/tests/e2e/gremlindb/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/gremlindb/main.test.bicep @@ -153,6 +153,7 @@ module testDeployment '../../../main.bicep' = [ principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -175,9 +176,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/document-db/database-account/tests/e2e/kvSecrets/dependencies.bicep b/avm/res/document-db/database-account/tests/e2e/kvSecrets/dependencies.bicep index caa4813d58..d3eadbfb8f 100644 --- a/avm/res/document-db/database-account/tests/e2e/kvSecrets/dependencies.bicep +++ b/avm/res/document-db/database-account/tests/e2e/kvSecrets/dependencies.bicep @@ -17,5 +17,5 @@ resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { } } -@description('The name of the Key Vault created.') -output keyVaultName string = keyVaultName +@description('The resource Id of the Key Vault created.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/document-db/database-account/tests/e2e/kvSecrets/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/kvSecrets/main.test.bicep index aa68b074b5..d8fef1215b 100644 --- a/avm/res/document-db/database-account/tests/e2e/kvSecrets/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/kvSecrets/main.test.bicep @@ -48,9 +48,24 @@ module testDeployment '../../../main.bicep' = { params: { location: enforcedLocation name: '${namePrefix}-kv-ref' - secretsKeyVault: { - keyVaultName: nestedDependencies.outputs.keyVaultName - primaryReadonlyConnectionStringSecretName: 'custom-secret-name' + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + primaryReadOnlyKeySecretName: 'primaryReadOnlyKey' + primaryWriteKeySecretName: 'primaryWriteKey' + primaryReadonlyConnectionStringSecretName: 'primaryReadonlyConnectionString' + primaryWriteConnectionStringSecretName: 'primaryWriteConnectionString' + secondaryReadonlyConnectionStringSecretName: 'secondaryReadonlyConnectionString' + secondaryReadonlyKeySecretName: 'secondaryReadonlyKey' + secondaryWriteConnectionStringSecretName: 'secondaryWriteConnectionString' + secondaryWriteKeySecretName: 'secondaryWriteKey' } } } + +// Output usage examples +output specificSecret string = testDeployment.outputs.exportedSecrets.primaryReadOnlyKey.secretResourceId +output allEportedSecrets object = testDeployment.outputs.exportedSecrets +output allExportedSecretResourceIds array = map( + items(testDeployment.outputs.exportedSecrets), + item => item.value.secretResourceId +) diff --git a/avm/res/document-db/database-account/tests/e2e/managedIdentity/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/managedIdentity/main.test.bicep index 70e0dbae60..f2a640a096 100644 --- a/avm/res/document-db/database-account/tests/e2e/managedIdentity/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/managedIdentity/main.test.bicep @@ -65,6 +65,7 @@ module testDeployment '../../../main.bicep' = { principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -79,7 +80,4 @@ module testDeployment '../../../main.bicep' = { } ] } - dependsOn: [ - nestedDependencies - ] } diff --git a/avm/res/document-db/database-account/tests/e2e/mongodb/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/mongodb/main.test.bicep index c2c1f5f676..ce8606f89a 100644 --- a/avm/res/document-db/database-account/tests/e2e/mongodb/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/mongodb/main.test.bicep @@ -287,6 +287,7 @@ module testDeployment '../../../main.bicep' = [ principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -309,9 +310,5 @@ module testDeployment '../../../main.bicep' = [ Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } ] diff --git a/avm/res/document-db/database-account/tests/e2e/publicRestrictedAccess/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/publicRestrictedAccess/main.test.bicep index 0617252bd1..d9d20880fe 100644 --- a/avm/res/document-db/database-account/tests/e2e/publicRestrictedAccess/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/publicRestrictedAccess/main.test.bicep @@ -9,11 +9,9 @@ metadata description = 'This instance deploys the module with public network acc @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-documentdb.databaseaccounts-${serviceShort}-rg' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'dddapres' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -70,7 +68,4 @@ module testDeployment '../../../main.bicep' = { } ] } - dependsOn: [ - nestedDependencies - ] } diff --git a/avm/res/document-db/database-account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/document-db/database-account/tests/e2e/waf-aligned/main.test.bicep index 6d09badaaf..1084431aa2 100644 --- a/avm/res/document-db/database-account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/document-db/database-account/tests/e2e/waf-aligned/main.test.bicep @@ -9,11 +9,9 @@ metadata description = 'This instance deploys the module in alignment with the b @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-documentdb.databaseaccounts-${serviceShort}-rg' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'dddawaf' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -84,9 +82,13 @@ module testDeployment '../../../main.bicep' = { ] privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'Sql' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -116,8 +118,4 @@ module testDeployment '../../../main.bicep' = { Role: 'DeploymentValidation' } } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] } diff --git a/avm/res/document-db/database-account/version.json b/avm/res/document-db/database-account/version.json index a8eda31021..e42c3d9e5f 100644 --- a/avm/res/document-db/database-account/version.json +++ b/avm/res/document-db/database-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/domain/README.md b/avm/res/event-grid/domain/README.md index 11e5d6818e..1618d77e52 100644 --- a/avm/res/event-grid/domain/README.md +++ b/avm/res/event-grid/domain/README.md @@ -20,8 +20,8 @@ This module deploys an Event Grid Domain. | `Microsoft.EventGrid/domains` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2023-06-01-preview/domains) | | `Microsoft.EventGrid/domains/topics` | [2022-06-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2022-06-15/domains/topics) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -126,9 +126,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -137,19 +141,25 @@ module domain 'br/public:avm/res/event-grid/domain:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: '1d2dba39-c8fe-45f9-a3af-6dc15caa95a5' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -225,9 +235,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -236,9 +250,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -246,11 +264,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { "roleAssignments": { "value": [ { + "name": "1d2dba39-c8fe-45f9-a3af-6dc15caa95a5", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -319,9 +339,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'domain' subnetResourceId: '' tags: { @@ -391,9 +415,13 @@ module domain 'br/public:avm/res/event-grid/domain:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "domain", "subnetResourceId": "", "tags": { @@ -743,8 +771,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -928,19 +955,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -978,6 +1050,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1028,6 +1101,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1097,6 +1177,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1147,6 +1228,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1185,6 +1273,7 @@ The topic names which are associated with the domain. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the event grid domain. | +| `privateEndpoints` | array | The private endpoints of the event grid domain. | | `resourceGroupName` | string | The name of the resource group the event grid domain was deployed into. | | `resourceId` | string | The resource ID of the event grid domain. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1195,7 +1284,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-grid/domain/main.bicep b/avm/res/event-grid/domain/main.bicep index 74229b9c89..5c6faac2cc 100644 --- a/avm/res/event-grid/domain/main.bicep +++ b/avm/res/event-grid/domain/main.bicep @@ -97,6 +97,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -185,7 +196,7 @@ resource domain_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-0 } ] -module domain_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module domain_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-domain-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -226,8 +237,7 @@ module domain_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -239,14 +249,10 @@ module domain_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1 ] resource domain_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(domain.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(domain.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -273,6 +279,17 @@ output location string = domain.location @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = domain.?identity.?principalId ?? '' +@description('The private endpoints of the event grid domain.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: domain_privateEndpoints[i].outputs.name + resourceId: domain_privateEndpoints[i].outputs.resourceId + groupId: domain_privateEndpoints[i].outputs.groupId + customDnsConfig: domain_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: domain_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -294,6 +311,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -332,11 +352,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/event-grid/domain/main.json b/avm/res/event-grid/domain/main.json index ffa934c004..3c794e8617 100644 --- a/avm/res/event-grid/domain/main.json +++ b/avm/res/event-grid/domain/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17426938291126651589" + "version": "0.29.47.4906", + "templateHash": "15360544071637596011" }, "name": "Event Grid Domains", "description": "This module deploys an Event Grid Domain.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -552,6 +582,13 @@ } }, "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'), 'userAssignedResourcesIds'), 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'), 'userAssignedResourcesIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -660,20 +697,20 @@ "domain_roleAssignments": { "copy": { "name": "domain_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/domains/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/domains', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/domains', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "domain" @@ -706,8 +743,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3734443194724604904" + "version": "0.29.47.4906", + "templateHash": "18115448694482617097" }, "name": "Event Grid Domain Topics", "description": "This module deploys an Event Grid Domain Topic.", @@ -795,11 +832,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -827,19 +861,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -943,13 +1005,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -983,8 +1045,11 @@ "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." + "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": { @@ -1024,8 +1089,11 @@ "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." + "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": { @@ -1073,6 +1141,29 @@ } }, "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": { @@ -1108,18 +1199,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1175,6 +1259,13 @@ } }, "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')]", @@ -1192,8 +1283,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1211,7 +1302,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1252,27 +1343,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1283,28 +1374,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1312,12 +1427,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1331,27 +1449,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1409,14 +1536,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1461,6 +1602,22 @@ "description": "The principal ID of the system assigned identity." }, "value": "[coalesce(tryGet(tryGet(reference('domain', '2023-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the event grid domain." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('domain_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('domain_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('domain_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('domain_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('domain_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/event-grid/domain/tests/e2e/max/main.test.bicep b/avm/res/event-grid/domain/tests/e2e/max/main.test.bicep index eb86e5c6ea..260afa0d4a 100644 --- a/avm/res/event-grid/domain/tests/e2e/max/main.test.bicep +++ b/avm/res/event-grid/domain/tests/e2e/max/main.test.bicep @@ -93,9 +93,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -104,19 +108,25 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] roleAssignments: [ { + name: '1d2dba39-c8fe-45f9-a3af-6dc15caa95a5' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep b/avm/res/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep index 6f5ed2bdbd..5c9ac482a6 100644 --- a/avm/res/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/event-grid/domain/tests/e2e/waf-aligned/main.test.bicep @@ -87,9 +87,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'domain' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { diff --git a/avm/res/event-grid/domain/topic/main.json b/avm/res/event-grid/domain/topic/main.json index 56d9c7b0a9..6d1c954eae 100644 --- a/avm/res/event-grid/domain/topic/main.json +++ b/avm/res/event-grid/domain/topic/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3734443194724604904" + "version": "0.29.47.4906", + "templateHash": "18115448694482617097" }, "name": "Event Grid Domain Topics", "description": "This module deploys an Event Grid Domain Topic.", diff --git a/avm/res/event-grid/domain/version.json b/avm/res/event-grid/domain/version.json index 76049e1c4a..ea4f3b6e67 100644 --- a/avm/res/event-grid/domain/version.json +++ b/avm/res/event-grid/domain/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/namespace/README.md b/avm/res/event-grid/namespace/README.md index 3be6075be5..658d780af8 100644 --- a/avm/res/event-grid/namespace/README.md +++ b/avm/res/event-grid/namespace/README.md @@ -26,8 +26,8 @@ This module deploys an Event Grid Namespace. | `Microsoft.EventGrid/namespaces/topics/eventSubscriptions` | [2023-12-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2023-12-15-preview/namespaces/topics/eventSubscriptions) | | `Microsoft.EventGrid/namespaces/topicSpaces` | [2023-12-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2023-12-15-preview/namespaces/topicSpaces) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -134,9 +134,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -145,19 +149,25 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: 'bde32b53-e30c-41d0-a338-c637853fe524' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -337,9 +347,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -348,9 +362,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -358,11 +376,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { "roleAssignments": { "value": [ { + "name": "bde32b53-e30c-41d0-a338-c637853fe524", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1196,9 +1216,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { location: '' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -1207,9 +1231,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -1272,9 +1300,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -1283,9 +1315,13 @@ module namespace 'br/public:avm/res/event-grid/namespace:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -1706,10 +1742,9 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | -| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1881,7 +1916,7 @@ Specify the name of lock. ### Parameter: `privateEndpoints.manualConnectionRequestMessage` -A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars. +A message passed to the owner of the remote resource with the manual connection request. - Required: No - Type: string @@ -1893,19 +1928,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1943,6 +2023,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1993,6 +2074,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2061,6 +2149,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2111,6 +2200,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2185,6 +2281,7 @@ Indicates if Topic Spaces Configuration is enabled for the namespace. This enabl | :-- | :-- | :-- | | `location` | string | The location the EventGrid Namespace was deployed into. | | `name` | string | The name of the EventGrid Namespace. | +| `privateEndpoints` | array | The private endpoints of the EventGrid Namespace. | | `resourceGroupName` | string | The name of the resource group the EventGrid Namespace was created in. | | `resourceId` | string | The resource ID of the EventGrid Namespace. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -2196,7 +2293,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-grid/namespace/ca-certificate/main.json b/avm/res/event-grid/namespace/ca-certificate/main.json index cfe3ff956e..ac1ee23280 100644 --- a/avm/res/event-grid/namespace/ca-certificate/main.json +++ b/avm/res/event-grid/namespace/ca-certificate/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12117465982320735659" + "version": "0.29.47.4906", + "templateHash": "12750364857938782710" }, "name": "Eventgrid Namespace CA Certificates", "description": "This module deploys an Eventgrid Namespace CA Certificate.", diff --git a/avm/res/event-grid/namespace/client-group/main.json b/avm/res/event-grid/namespace/client-group/main.json index d05e3b6175..2c699d01b0 100644 --- a/avm/res/event-grid/namespace/client-group/main.json +++ b/avm/res/event-grid/namespace/client-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8957170895657751475" + "version": "0.29.47.4906", + "templateHash": "16551042161872604300" }, "name": "Eventgrid Namespace Client Groups", "description": "This module deploys an Eventgrid Namespace Client Group.", diff --git a/avm/res/event-grid/namespace/client/main.json b/avm/res/event-grid/namespace/client/main.json index d9df0ea692..01ecbb05fa 100644 --- a/avm/res/event-grid/namespace/client/main.json +++ b/avm/res/event-grid/namespace/client/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9304498064315745543" + "version": "0.29.47.4906", + "templateHash": "10456542411882263533" }, "name": "Eventgrid Namespace Clients", "description": "This module deploys an Eventgrid Namespace Client.", diff --git a/avm/res/event-grid/namespace/main.bicep b/avm/res/event-grid/namespace/main.bicep index 7ffed8e047..29c68aeddc 100644 --- a/avm/res/event-grid/namespace/main.bicep +++ b/avm/res/event-grid/namespace/main.bicep @@ -160,6 +160,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -257,7 +268,7 @@ resource namespace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 } ] -module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -298,8 +309,7 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -311,14 +321,10 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. ] resource namespace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(namespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(namespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -440,6 +446,17 @@ output topicResourceIds array = [ for index in range(0, length(topics ?? [])): namespace_topics[index].outputs.resourceId ] +@description('The private endpoints of the EventGrid Namespace.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: namespace_privateEndpoints[i].outputs.name + resourceId: namespace_privateEndpoints[i].outputs.resourceId + groupId: namespace_privateEndpoints[i].outputs.groupId + customDnsConfig: namespace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: namespace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // ================ // // Definitions // // ================ // @@ -461,6 +478,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -499,16 +519,26 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? - @description('Optional. A message passed to the owner of the remote resource with the manual connection request. Restricted to 140 chars.') + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) manualConnectionRequestMessage: string? @description('Optional. Custom DNS configurations.') diff --git a/avm/res/event-grid/namespace/main.json b/avm/res/event-grid/namespace/main.json index f750a19ee9..740f8c02be 100644 --- a/avm/res/event-grid/namespace/main.json +++ b/avm/res/event-grid/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10205244849642221535" + "version": "0.29.47.4906", + "templateHash": "12641353733245664102" }, "name": "Event Grid Namespaces", "description": "This module deploys an Event Grid Namespace.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -193,8 +223,9 @@ "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. Restricted to 140 chars." + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." } }, "customDnsConfigs": { @@ -638,6 +669,13 @@ } }, "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": { @@ -749,20 +787,20 @@ "namespace_roleAssignments": { "copy": { "name": "namespace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "namespace" @@ -800,11 +838,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -832,19 +867,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -948,13 +1011,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -988,8 +1051,11 @@ "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." + "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": { @@ -1029,8 +1095,11 @@ "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." + "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": { @@ -1078,6 +1147,29 @@ } }, "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": { @@ -1113,18 +1205,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1180,6 +1265,13 @@ } }, "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')]", @@ -1197,8 +1289,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1216,7 +1308,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1257,27 +1349,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1288,28 +1380,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1317,12 +1433,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1336,27 +1455,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1414,14 +1542,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1473,8 +1615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1744917828950358230" + "version": "0.29.47.4906", + "templateHash": "2384224485883500454" }, "name": "Eventgrid Namespace Topics", "description": "This module deploys an Eventgrid Namespace Topic.", @@ -1511,6 +1653,13 @@ "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": { @@ -1630,6 +1779,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -1683,20 +1839,20 @@ "topic_roleAssignments": { "copy": { "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topics/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topic" @@ -1745,8 +1901,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16670830960405846481" + "version": "0.29.47.4906", + "templateHash": "484752655079146569" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription.", @@ -1758,6 +1914,13 @@ "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": { @@ -1868,6 +2031,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -1916,20 +2086,20 @@ "eventSubscription_roleAssignments": { "copy": { "name": "eventSubscription_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topics/{1}/eventSubscriptions/{2}', parameters('namespaceName'), parameters('topicName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventSubscription" @@ -2031,8 +2201,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12117465982320735659" + "version": "0.29.47.4906", + "templateHash": "12750364857938782710" }, "name": "Eventgrid Namespace CA Certificates", "description": "This module deploys an Eventgrid Namespace CA Certificate.", @@ -2161,8 +2331,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9304498064315745543" + "version": "0.29.47.4906", + "templateHash": "10456542411882263533" }, "name": "Eventgrid Namespace Clients", "description": "This module deploys an Eventgrid Namespace Client.", @@ -2327,8 +2497,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8957170895657751475" + "version": "0.29.47.4906", + "templateHash": "16551042161872604300" }, "name": "Eventgrid Namespace Client Groups", "description": "This module deploys an Eventgrid Namespace Client Group.", @@ -2448,8 +2618,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16208314286026284609" + "version": "0.29.47.4906", + "templateHash": "13412075546334276790" }, "name": "Eventgrid Namespace Topic Spaces", "description": "This module deploys an Eventgrid Namespace Topic Space.", @@ -2461,6 +2631,13 @@ "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": { @@ -2558,6 +2735,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -2593,23 +2777,23 @@ "namespace" ] }, - "topic_roleAssignments": { + "topicSpace_roleAssignments": { "copy": { - "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "topicSpace_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topicSpaces/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topicSpaces', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topicSpaces', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topicSpace" @@ -2686,8 +2870,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17753700427881740340" + "version": "0.29.47.4906", + "templateHash": "8789192089964505787" }, "name": "Eventgrid Namespace Permissions Bindings", "description": "This module deploys an Eventgrid Namespace Permission Binding.", @@ -2835,6 +3019,22 @@ "count": "[length(range(0, length(coalesce(parameters('topics'), createArray()))))]", "input": "[reference(format('namespace_topics[{0}]', range(0, length(coalesce(parameters('topics'), createArray())))[copyIndex()])).outputs.resourceId.value]" } + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the EventGrid Namespace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/event-grid/namespace/permission-binding/main.json b/avm/res/event-grid/namespace/permission-binding/main.json index 2d00ef8a80..48f7e420f5 100644 --- a/avm/res/event-grid/namespace/permission-binding/main.json +++ b/avm/res/event-grid/namespace/permission-binding/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17753700427881740340" + "version": "0.29.47.4906", + "templateHash": "8789192089964505787" }, "name": "Eventgrid Namespace Permissions Bindings", "description": "This module deploys an Eventgrid Namespace Permission Binding.", diff --git a/avm/res/event-grid/namespace/tests/e2e/max/main.test.bicep b/avm/res/event-grid/namespace/tests/e2e/max/main.test.bicep index bc7f73489c..53ac497b38 100644 --- a/avm/res/event-grid/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/event-grid/namespace/tests/e2e/max/main.test.bicep @@ -96,9 +96,13 @@ module testDeployment '../../../main.bicep' = [ privateEndpoints: [ { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -107,18 +111,24 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] roleAssignments: [ { + name: 'bde32b53-e30c-41d0-a338-c637853fe524' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/event-grid/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/event-grid/namespace/tests/e2e/waf-aligned/main.test.bicep index a7f5df77a0..c91a4e4ab4 100644 --- a/avm/res/event-grid/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/event-grid/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -78,9 +78,13 @@ module testDeployment '../../../main.bicep' = [ privateEndpoints: [ { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -89,9 +93,13 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] roleAssignments: [ diff --git a/avm/res/event-grid/namespace/topic-space/README.md b/avm/res/event-grid/namespace/topic-space/README.md index 4fc1929791..85b5b0afe8 100644 --- a/avm/res/event-grid/namespace/topic-space/README.md +++ b/avm/res/event-grid/namespace/topic-space/README.md @@ -89,6 +89,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -139,6 +140,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/event-grid/namespace/topic-space/main.bicep b/avm/res/event-grid/namespace/topic-space/main.bicep index 23cec86c04..d79d805413 100644 --- a/avm/res/event-grid/namespace/topic-space/main.bicep +++ b/avm/res/event-grid/namespace/topic-space/main.bicep @@ -64,6 +64,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -81,15 +92,11 @@ resource topicSpace 'Microsoft.EventGrid/namespaces/topicSpaces@2023-12-15-previ } } -resource topic_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(topicSpace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) +resource topicSpace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(topicSpace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -119,6 +126,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/event-grid/namespace/topic-space/main.json b/avm/res/event-grid/namespace/topic-space/main.json index 8b396c4c8c..1b59f33e4c 100644 --- a/avm/res/event-grid/namespace/topic-space/main.json +++ b/avm/res/event-grid/namespace/topic-space/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16208314286026284609" + "version": "0.29.47.4906", + "templateHash": "13412075546334276790" }, "name": "Eventgrid Namespace Topic Spaces", "description": "This module deploys an Eventgrid Namespace Topic Space.", @@ -18,6 +18,13 @@ "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": { @@ -115,6 +122,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -150,23 +164,23 @@ "namespace" ] }, - "topic_roleAssignments": { + "topicSpace_roleAssignments": { "copy": { - "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "topicSpace_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topicSpaces/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topicSpaces', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topicSpaces', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topicSpace" diff --git a/avm/res/event-grid/namespace/topic/README.md b/avm/res/event-grid/namespace/topic/README.md index 234d969644..8a5a7cadd3 100644 --- a/avm/res/event-grid/namespace/topic/README.md +++ b/avm/res/event-grid/namespace/topic/README.md @@ -147,6 +147,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -197,6 +198,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/event-grid/namespace/topic/event-subscription/README.md b/avm/res/event-grid/namespace/topic/event-subscription/README.md index 9bedd147ad..8925aa220a 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/README.md +++ b/avm/res/event-grid/namespace/topic/event-subscription/README.md @@ -106,6 +106,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -156,6 +157,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/event-grid/namespace/topic/event-subscription/main.bicep b/avm/res/event-grid/namespace/topic/event-subscription/main.bicep index 553032cc47..f59486ced5 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/main.bicep +++ b/avm/res/event-grid/namespace/topic/event-subscription/main.bicep @@ -69,6 +69,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -92,14 +103,14 @@ resource eventSubscription 'Microsoft.EventGrid/namespaces/topics/eventSubscript } resource eventSubscription_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(eventSubscription.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + eventSubscription.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -131,6 +142,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/event-grid/namespace/topic/event-subscription/main.json b/avm/res/event-grid/namespace/topic/event-subscription/main.json index 5ee4b98584..3c60ffd081 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/main.json +++ b/avm/res/event-grid/namespace/topic/event-subscription/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16670830960405846481" + "version": "0.29.47.4906", + "templateHash": "484752655079146569" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription.", @@ -18,6 +18,13 @@ "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": { @@ -128,6 +135,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -176,20 +190,20 @@ "eventSubscription_roleAssignments": { "copy": { "name": "eventSubscription_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topics/{1}/eventSubscriptions/{2}', parameters('namespaceName'), parameters('topicName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventSubscription" diff --git a/avm/res/event-grid/namespace/topic/main.bicep b/avm/res/event-grid/namespace/topic/main.bicep index 46dc9660fd..7253eb78c5 100644 --- a/avm/res/event-grid/namespace/topic/main.bicep +++ b/avm/res/event-grid/namespace/topic/main.bicep @@ -74,6 +74,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -104,14 +115,10 @@ resource topic_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock } resource topic_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -164,6 +171,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/event-grid/namespace/topic/main.json b/avm/res/event-grid/namespace/topic/main.json index bf36d6cfb9..a66e54d064 100644 --- a/avm/res/event-grid/namespace/topic/main.json +++ b/avm/res/event-grid/namespace/topic/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1744917828950358230" + "version": "0.29.47.4906", + "templateHash": "2384224485883500454" }, "name": "Eventgrid Namespace Topics", "description": "This module deploys an Eventgrid Namespace Topic.", @@ -43,6 +43,13 @@ "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": { @@ -162,6 +169,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -215,20 +229,20 @@ "topic_roleAssignments": { "copy": { "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topics/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topic" @@ -277,8 +291,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16670830960405846481" + "version": "0.29.47.4906", + "templateHash": "484752655079146569" }, "name": "Event Subscriptions", "description": "This module deploys an Event Subscription.", @@ -290,6 +304,13 @@ "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": { @@ -400,6 +421,13 @@ } }, "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": { "Azure Resource Notifications System Topics Subscriber": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0b962ed2-6d56-471c-bd5f-3477d83a7ba4')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -448,20 +476,20 @@ "eventSubscription_roleAssignments": { "copy": { "name": "eventSubscription_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/namespaces/{0}/topics/{1}/eventSubscriptions/{2}', parameters('namespaceName'), parameters('topicName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/namespaces/topics/eventSubscriptions', parameters('namespaceName'), parameters('topicName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventSubscription" diff --git a/avm/res/event-grid/namespace/version.json b/avm/res/event-grid/namespace/version.json index 729ac87673..13669e6601 100644 --- a/avm/res/event-grid/namespace/version.json +++ b/avm/res/event-grid/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/system-topic/README.md b/avm/res/event-grid/system-topic/README.md index 23cec997e1..fcd58d0516 100644 --- a/avm/res/event-grid/system-topic/README.md +++ b/avm/res/event-grid/system-topic/README.md @@ -154,11 +154,13 @@ module systemTopic 'br/public:avm/res/event-grid/system-topic:' = { } roleAssignments: [ { + name: 'c9beca28-efcf-4d1d-99aa-8f334484a2c2' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -259,11 +261,13 @@ module systemTopic 'br/public:avm/res/event-grid/system-topic:' = { "roleAssignments": { "value": [ { + "name": "c9beca28-efcf-4d1d-99aa-8f334484a2c2", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -746,6 +750,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -796,6 +801,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/event-grid/system-topic/event-subscription/main.json b/avm/res/event-grid/system-topic/event-subscription/main.json index fe7bff1808..e0c740255c 100644 --- a/avm/res/event-grid/system-topic/event-subscription/main.json +++ b/avm/res/event-grid/system-topic/event-subscription/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4152124273759917390" + "version": "0.29.47.4906", + "templateHash": "17003151222134625001" }, "name": "Event Grid System Topic Event Subscriptions", "description": "This module deploys an Event Grid System Topic Event Subscription.", diff --git a/avm/res/event-grid/system-topic/main.bicep b/avm/res/event-grid/system-topic/main.bicep index 024aec0346..9673756db5 100644 --- a/avm/res/event-grid/system-topic/main.bicep +++ b/avm/res/event-grid/system-topic/main.bicep @@ -80,6 +80,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -182,14 +193,10 @@ resource systemTopic_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2 ] resource systemTopic_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(systemTopic.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(systemTopic.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -237,6 +244,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/event-grid/system-topic/main.json b/avm/res/event-grid/system-topic/main.json index 3ed97b7179..36a34561c0 100644 --- a/avm/res/event-grid/system-topic/main.json +++ b/avm/res/event-grid/system-topic/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18054095747120570835" + "version": "0.29.47.4906", + "templateHash": "18332319378144663212" }, "name": "Event Grid System Topics", "description": "This module deploys an Event Grid System Topic.", @@ -66,6 +66,13 @@ "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": { @@ -321,6 +328,13 @@ } }, "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'), 'userAssignedResourcesIds'), 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'), 'userAssignedResourcesIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -426,20 +440,20 @@ "systemTopic_roleAssignments": { "copy": { "name": "systemTopic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/systemTopics/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/systemTopics', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/systemTopics', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "systemTopic" @@ -483,8 +497,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4152124273759917390" + "version": "0.29.47.4906", + "templateHash": "17003151222134625001" }, "name": "Event Grid System Topic Event Subscriptions", "description": "This module deploys an Event Grid System Topic Event Subscription.", diff --git a/avm/res/event-grid/system-topic/tests/e2e/max/main.test.bicep b/avm/res/event-grid/system-topic/tests/e2e/max/main.test.bicep index c239434df1..2e8c8c4802 100644 --- a/avm/res/event-grid/system-topic/tests/e2e/max/main.test.bicep +++ b/avm/res/event-grid/system-topic/tests/e2e/max/main.test.bicep @@ -115,11 +115,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'c9beca28-efcf-4d1d-99aa-8f334484a2c2' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/event-grid/system-topic/version.json b/avm/res/event-grid/system-topic/version.json index daf1a794d9..76049e1c4a 100644 --- a/avm/res/event-grid/system-topic/version.json +++ b/avm/res/event-grid/system-topic/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/event-grid/topic/README.md b/avm/res/event-grid/topic/README.md index c589fdc2a3..f318f123ff 100644 --- a/avm/res/event-grid/topic/README.md +++ b/avm/res/event-grid/topic/README.md @@ -20,8 +20,8 @@ This module deploys an Event Grid Topic. | `Microsoft.EventGrid/topics` | [2021-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2021-06-01-preview/topics) | | `Microsoft.EventGrid/topics/eventSubscriptions` | [2022-06-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventGrid/2022-06-15/topics/eventSubscriptions) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -149,9 +149,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { principalId: '' @@ -172,19 +176,25 @@ module topic 'br/public:avm/res/event-grid/topic:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: 'f80d2f24-53f6-41b3-811f-668b2273dcf8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -282,9 +292,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { "principalId": "", @@ -305,9 +319,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -315,11 +333,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { "roleAssignments": { "value": [ { + "name": "f80d2f24-53f6-41b3-811f-668b2273dcf8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -406,9 +426,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'topic' subnetResourceId: '' tags: { @@ -500,9 +524,13 @@ module topic 'br/public:avm/res/event-grid/topic:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "topic", "subnetResourceId": "", "tags": { @@ -837,8 +865,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1022,19 +1049,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1072,6 +1144,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1122,6 +1195,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1191,6 +1271,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1241,6 +1322,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1272,6 +1360,7 @@ Tags of the resource. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the event grid topic. | +| `privateEndpoints` | array | The private endpoints of the event grid topic. | | `resourceGroupName` | string | The name of the resource group the event grid topic was deployed into. | | `resourceId` | string | The resource ID of the event grid topic. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1282,7 +1371,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-grid/topic/event-subscription/main.json b/avm/res/event-grid/topic/event-subscription/main.json index 045746b418..7c0ad57b58 100644 --- a/avm/res/event-grid/topic/event-subscription/main.json +++ b/avm/res/event-grid/topic/event-subscription/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8224561812471787120" + "version": "0.29.47.4906", + "templateHash": "2521126347714866146" }, "name": "EventGrid Topic Event Subscriptions", "description": "This module deploys an Event Grid Topic Event Subscription.", diff --git a/avm/res/event-grid/topic/main.bicep b/avm/res/event-grid/topic/main.bicep index 3d8f65a61b..400d3c498b 100644 --- a/avm/res/event-grid/topic/main.bicep +++ b/avm/res/event-grid/topic/main.bicep @@ -91,6 +91,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -195,7 +206,7 @@ resource topic_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05 } ] -module topic_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module topic_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-topic-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -236,8 +247,7 @@ module topic_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -249,14 +259,10 @@ module topic_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' ] resource topic_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -283,6 +289,17 @@ output location string = topic.location @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = topic.?identity.?principalId ?? '' +@description('The private endpoints of the event grid topic.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: topic_privateEndpoints[i].outputs.name + resourceId: topic_privateEndpoints[i].outputs.resourceId + groupId: topic_privateEndpoints[i].outputs.groupId + customDnsConfig: topic_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: topic_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -304,6 +321,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -342,11 +362,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/event-grid/topic/main.json b/avm/res/event-grid/topic/main.json index bcf10b8ae8..8e1ca6e46e 100644 --- a/avm/res/event-grid/topic/main.json +++ b/avm/res/event-grid/topic/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3421506358076457730" + "version": "0.29.47.4906", + "templateHash": "14857992424143462934" }, "name": "Event Grid Topics", "description": "This module deploys an Event Grid Topic.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -538,6 +568,13 @@ } }, "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'), 'userAssignedResourcesIds'), 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'), 'userAssignedResourcesIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -644,20 +681,20 @@ "topic_roleAssignments": { "copy": { "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventGrid/topics/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventGrid/topics', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventGrid/topics', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topic" @@ -701,8 +738,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8224561812471787120" + "version": "0.29.47.4906", + "templateHash": "2521126347714866146" }, "name": "EventGrid Topic Event Subscriptions", "description": "This module deploys an Event Grid Topic Event Subscription.", @@ -876,11 +913,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -908,19 +942,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1024,13 +1086,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1064,8 +1126,11 @@ "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." + "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": { @@ -1105,8 +1170,11 @@ "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." + "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": { @@ -1154,6 +1222,29 @@ } }, "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": { @@ -1189,18 +1280,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1256,6 +1340,13 @@ } }, "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')]", @@ -1273,8 +1364,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1292,7 +1383,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1333,27 +1424,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1364,28 +1455,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1393,12 +1508,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1412,27 +1530,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1490,14 +1617,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1542,6 +1683,22 @@ "description": "The principal ID of the system assigned identity." }, "value": "[coalesce(tryGet(tryGet(reference('topic', '2021-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the event grid topic." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('topic_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('topic_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('topic_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('topic_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('topic_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/event-grid/topic/tests/e2e/max/main.test.bicep b/avm/res/event-grid/topic/tests/e2e/max/main.test.bicep index 6c4015fec7..d68984a6c2 100644 --- a/avm/res/event-grid/topic/tests/e2e/max/main.test.bicep +++ b/avm/res/event-grid/topic/tests/e2e/max/main.test.bicep @@ -118,9 +118,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId roleAssignments: [ { @@ -144,19 +148,25 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] roleAssignments: [ { + name: 'f80d2f24-53f6-41b3-811f-668b2273dcf8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep b/avm/res/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep index aec73401ac..b3ffdf3c21 100644 --- a/avm/res/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/event-grid/topic/tests/e2e/waf-aligned/main.test.bicep @@ -113,9 +113,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'topic' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { diff --git a/avm/res/event-grid/topic/version.json b/avm/res/event-grid/topic/version.json index 76049e1c4a..ea4f3b6e67 100644 --- a/avm/res/event-grid/topic/version.json +++ b/avm/res/event-grid/topic/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-hub/namespace/README.md b/avm/res/event-hub/namespace/README.md index 9ad38386f2..ad3b0d255d 100644 --- a/avm/res/event-hub/namespace/README.md +++ b/avm/res/event-hub/namespace/README.md @@ -25,8 +25,8 @@ This module deploys an Event Hub Namespace. | `Microsoft.EventHub/namespaces/eventhubs/consumergroups` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2024-01-01/namespaces/eventhubs/consumergroups) | | `Microsoft.EventHub/namespaces/networkRuleSets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.EventHub/2024-01-01/namespaces/networkRuleSets) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -317,9 +317,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' tags: { @@ -332,11 +336,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: 'bd0f41e3-8e3e-4cd3-b028-edd61608bd9f' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -531,9 +537,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "", "tags": { @@ -550,11 +560,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { "roleAssignments": { "value": [ { + "name": "bd0f41e3-8e3e-4cd3-b028-edd61608bd9f", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -710,9 +722,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -729,7 +745,6 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: true } } ``` @@ -877,9 +892,13 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -904,9 +923,6 @@ module namespace 'br/public:avm/res/event-hub/namespace:' = { "hidden-title": "This is visible in the resource name", "Role": "DeploymentValidation" } - }, - "zoneRedundant": { - "value": true } } } @@ -1353,8 +1369,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1538,19 +1553,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1588,6 +1648,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1638,6 +1699,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1716,6 +1784,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1766,6 +1835,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1820,7 +1896,7 @@ Switch to make the Event Hub Namespace zone redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs @@ -1830,6 +1906,7 @@ Switch to make the Event Hub Namespace zone redundant. | `eventHubResourceIds` | array | The Resources IDs of the EventHubs within this eventspace. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the eventspace. | +| `privateEndpoints` | array | The private endpoints of the eventspace. | | `resourceGroupName` | string | The resource group where the namespace is deployed. | | `resourceId` | string | The resource ID of the eventspace. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1840,7 +1917,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/event-hub/namespace/authorization-rule/main.json b/avm/res/event-hub/namespace/authorization-rule/main.json index 0f77545161..7e2d70ae02 100644 --- a/avm/res/event-hub/namespace/authorization-rule/main.json +++ b/avm/res/event-hub/namespace/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10221495621085164818" + "version": "0.29.47.4906", + "templateHash": "10257599829745692462" }, "name": "Event Hub Namespace Authorization Rule", "description": "This module deploys an Event Hub Namespace Authorization Rule.", diff --git a/avm/res/event-hub/namespace/disaster-recovery-config/main.json b/avm/res/event-hub/namespace/disaster-recovery-config/main.json index 37eb607817..685f056986 100644 --- a/avm/res/event-hub/namespace/disaster-recovery-config/main.json +++ b/avm/res/event-hub/namespace/disaster-recovery-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5063457131353044394" + "version": "0.29.47.4906", + "templateHash": "11417529326547724260" }, "name": "Event Hub Namespace Disaster Recovery Configs", "description": "This module deploys an Event Hub Namespace Disaster Recovery Config.", diff --git a/avm/res/event-hub/namespace/eventhub/README.md b/avm/res/event-hub/namespace/eventhub/README.md index 6237affcf2..c8ddee5c37 100644 --- a/avm/res/event-hub/namespace/eventhub/README.md +++ b/avm/res/event-hub/namespace/eventhub/README.md @@ -291,6 +291,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -341,6 +342,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/event-hub/namespace/eventhub/authorization-rule/main.json b/avm/res/event-hub/namespace/eventhub/authorization-rule/main.json index 30dd4694be..ac4068b94f 100644 --- a/avm/res/event-hub/namespace/eventhub/authorization-rule/main.json +++ b/avm/res/event-hub/namespace/eventhub/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4404779525744622109" + "version": "0.29.47.4906", + "templateHash": "15510112090713187287" }, "name": "Event Hub Namespace Event Hub Authorization Rules", "description": "This module deploys an Event Hub Namespace Event Hub Authorization Rule.", diff --git a/avm/res/event-hub/namespace/eventhub/consumergroup/main.json b/avm/res/event-hub/namespace/eventhub/consumergroup/main.json index 00354dedef..50a1211448 100644 --- a/avm/res/event-hub/namespace/eventhub/consumergroup/main.json +++ b/avm/res/event-hub/namespace/eventhub/consumergroup/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8005690025378324644" + "version": "0.29.47.4906", + "templateHash": "3193397457060310848" }, "name": "Event Hub Namespace Event Hub Consumer Groups", "description": "This module deploys an Event Hub Namespace Event Hub Consumer Group.", diff --git a/avm/res/event-hub/namespace/eventhub/main.bicep b/avm/res/event-hub/namespace/eventhub/main.bicep index a0e4be604f..e3c6fe3091 100644 --- a/avm/res/event-hub/namespace/eventhub/main.bicep +++ b/avm/res/event-hub/namespace/eventhub/main.bicep @@ -168,6 +168,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource namespace 'Microsoft.EventHub/namespaces@2024-01-01' existing = { name: namespaceName } @@ -214,14 +225,10 @@ module eventHub_authorizationRules 'authorization-rule/main.bicep' = [ ] resource eventHub_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(eventHub.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(eventHub.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -255,6 +262,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/event-hub/namespace/eventhub/main.json b/avm/res/event-hub/namespace/eventhub/main.json index 9877ca8b9c..c3910acbe4 100644 --- a/avm/res/event-hub/namespace/eventhub/main.json +++ b/avm/res/event-hub/namespace/eventhub/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10950140258950515541" + "version": "0.29.47.4906", + "templateHash": "16769928083256444746" }, "name": "Event Hub Namespace Event Hubs", "description": "This module deploys an Event Hub Namespace Event Hub.", @@ -43,6 +43,13 @@ "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": { @@ -295,6 +302,13 @@ } }, "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)))))]" + } + ], "eventHubProperties": { "messageRetentionInDays": "[parameters('messageRetentionInDays')]", "partitionCount": "[parameters('partitionCount')]", @@ -366,20 +380,20 @@ "eventHub_roleAssignments": { "copy": { "name": "eventHub_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventHub/namespaces/{0}/eventhubs/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventHub/namespaces/eventhubs', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventHub/namespaces/eventhubs', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventHub" @@ -416,8 +430,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8005690025378324644" + "version": "0.29.47.4906", + "templateHash": "3193397457060310848" }, "name": "Event Hub Namespace Event Hub Consumer Groups", "description": "This module deploys an Event Hub Namespace Event Hub Consumer Group.", @@ -520,8 +534,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4404779525744622109" + "version": "0.29.47.4906", + "templateHash": "15510112090713187287" }, "name": "Event Hub Namespace Event Hub Authorization Rules", "description": "This module deploys an Event Hub Namespace Event Hub Authorization Rule.", diff --git a/avm/res/event-hub/namespace/main.bicep b/avm/res/event-hub/namespace/main.bicep index 1099137ddb..bc225dd8af 100644 --- a/avm/res/event-hub/namespace/main.bicep +++ b/avm/res/event-hub/namespace/main.bicep @@ -23,7 +23,7 @@ param skuName string = 'Standard' param skuCapacity int = 1 @description('Optional. Switch to make the Event Hub Namespace zone redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. Switch to enable the Auto Inflate feature of Event Hub. Auto Inflate is not supported in Premium SKU EventHub.') param isAutoInflateEnabled bool = false @@ -147,6 +147,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = if (!empty(customerManagedKey.?keyVaultResourceId)) { name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) scope: resourceGroup( @@ -338,7 +349,7 @@ module eventHubNamespace_networkRuleSet 'network-rule-set/main.bicep' = if (!emp } } -module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-eventHubNamespace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -379,8 +390,7 @@ module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-end 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -392,14 +402,14 @@ module eventHubNamespace_privateEndpoints 'br/public:avm/res/network/private-end ] resource eventHubNamespace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(eventHubNamespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + eventHubNamespace.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -471,6 +481,17 @@ output eventHubResourceIds array = [ for index in range(0, length(eventhubs ?? [])): eventHubNamespace_eventhubs[index].outputs.resourceId ] +@description('The private endpoints of the eventspace.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: eventHubNamespace_privateEndpoints[i].outputs.name + resourceId: eventHubNamespace_privateEndpoints[i].outputs.resourceId + groupId: eventHubNamespace_privateEndpoints[i].outputs.groupId + customDnsConfig: eventHubNamespace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: eventHubNamespace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -492,6 +513,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -530,11 +554,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/event-hub/namespace/main.json b/avm/res/event-hub/namespace/main.json index aed0f57e1c..7856e8e980 100644 --- a/avm/res/event-hub/namespace/main.json +++ b/avm/res/event-hub/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14498506767456149873" + "version": "0.29.47.4906", + "templateHash": "3566282798641159484" }, "name": "Event Hub Namespaces", "description": "This module deploys an Event Hub Namespace.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -515,7 +545,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Switch to make the Event Hub Namespace zone redundant." } @@ -671,6 +701,13 @@ } }, "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)))))]" + } + ], "maximumThroughputUnitsVar": "[if(not(parameters('isAutoInflateEnabled')), 0, parameters('maximumThroughputUnits'))]", "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())]", @@ -766,20 +803,20 @@ "eventHubNamespace_roleAssignments": { "copy": { "name": "eventHubNamespace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventHub/namespaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventHub/namespaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventHub/namespaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventHubNamespace" @@ -868,8 +905,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10221495621085164818" + "version": "0.29.47.4906", + "templateHash": "10257599829745692462" }, "name": "Event Hub Namespace Authorization Rule", "description": "This module deploys an Event Hub Namespace Authorization Rule.", @@ -965,8 +1002,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5063457131353044394" + "version": "0.29.47.4906", + "templateHash": "11417529326547724260" }, "name": "Event Hub Namespace Disaster Recovery Configs", "description": "This module deploys an Event Hub Namespace Disaster Recovery Config.", @@ -1081,8 +1118,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10950140258950515541" + "version": "0.29.47.4906", + "templateHash": "16769928083256444746" }, "name": "Event Hub Namespace Event Hubs", "description": "This module deploys an Event Hub Namespace Event Hub.", @@ -1119,6 +1156,13 @@ "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": { @@ -1371,6 +1415,13 @@ } }, "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)))))]" + } + ], "eventHubProperties": { "messageRetentionInDays": "[parameters('messageRetentionInDays')]", "partitionCount": "[parameters('partitionCount')]", @@ -1442,20 +1493,20 @@ "eventHub_roleAssignments": { "copy": { "name": "eventHub_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.EventHub/namespaces/{0}/eventhubs/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.EventHub/namespaces/eventhubs', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.EventHub/namespaces/eventhubs', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "eventHub" @@ -1492,8 +1543,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8005690025378324644" + "version": "0.29.47.4906", + "templateHash": "3193397457060310848" }, "name": "Event Hub Namespace Event Hub Consumer Groups", "description": "This module deploys an Event Hub Namespace Event Hub Consumer Group.", @@ -1596,8 +1647,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4404779525744622109" + "version": "0.29.47.4906", + "templateHash": "15510112090713187287" }, "name": "Event Hub Namespace Event Hub Authorization Rules", "description": "This module deploys an Event Hub Namespace Event Hub Authorization Rule.", @@ -1732,8 +1783,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17588066233658348958" + "version": "0.29.47.4906", + "templateHash": "10667940256299428520" }, "name": "Event Hub Namespace Network Rule Sets", "description": "This module deploys an Event Hub Namespace Network Rule Set.", @@ -1884,11 +1935,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1916,19 +1964,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2032,13 +2108,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2072,8 +2148,11 @@ "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." + "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": { @@ -2113,8 +2192,11 @@ "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." + "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": { @@ -2162,6 +2244,29 @@ } }, "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": { @@ -2197,18 +2302,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2264,6 +2362,13 @@ } }, "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')]", @@ -2281,8 +2386,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2300,7 +2405,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2341,27 +2446,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -2372,28 +2477,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -2401,12 +2530,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -2420,27 +2552,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2498,14 +2639,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2560,6 +2715,22 @@ "count": "[length(range(0, length(coalesce(parameters('eventhubs'), createArray()))))]", "input": "[reference(format('eventHubNamespace_eventhubs[{0}]', range(0, length(coalesce(parameters('eventhubs'), createArray())))[copyIndex()])).outputs.resourceId.value]" } + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the eventspace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('eventHubNamespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('eventHubNamespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('eventHubNamespace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('eventHubNamespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('eventHubNamespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/event-hub/namespace/network-rule-set/main.json b/avm/res/event-hub/namespace/network-rule-set/main.json index b0f29e60ae..b3c0987ca7 100644 --- a/avm/res/event-hub/namespace/network-rule-set/main.json +++ b/avm/res/event-hub/namespace/network-rule-set/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17588066233658348958" + "version": "0.29.47.4906", + "templateHash": "10667940256299428520" }, "name": "Event Hub Namespace Network Rule Sets", "description": "This module deploys an Event Hub Namespace Network Rule Set.", diff --git a/avm/res/event-hub/namespace/tests/e2e/max/main.test.bicep b/avm/res/event-hub/namespace/tests/e2e/max/main.test.bicep index b4f2b5c6c3..4c8d619668 100644 --- a/avm/res/event-hub/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/event-hub/namespace/tests/e2e/max/main.test.bicep @@ -187,9 +187,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -201,11 +205,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'bd0f41e3-8e3e-4cd3-b028-edd61608bd9f' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep index cf65bb6f5e..b4f8c06a13 100644 --- a/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/event-hub/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -65,7 +65,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - zoneRedundant: true skuName: 'Standard' skuCapacity: 2 authorizationRules: [ @@ -167,9 +166,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' diff --git a/avm/res/event-hub/namespace/version.json b/avm/res/event-hub/namespace/version.json index c177b1bb58..e42c3d9e5f 100644 --- a/avm/res/event-hub/namespace/version.json +++ b/avm/res/event-hub/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/health-bot/health-bot/README.md b/avm/res/health-bot/health-bot/README.md index 8425995006..f730e933a5 100644 --- a/avm/res/health-bot/health-bot/README.md +++ b/avm/res/health-bot/health-bot/README.md @@ -117,11 +117,13 @@ module healthBot 'br/public:avm/res/health-bot/health-bot:' = { } roleAssignments: [ { + name: '9d89b5ea-0d1f-41d8-9297-52529827d712' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -180,11 +182,13 @@ module healthBot 'br/public:avm/res/health-bot/health-bot:' = { "roleAssignments": { "value": [ { + "name": "9d89b5ea-0d1f-41d8-9297-52529827d712", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -421,6 +425,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -471,6 +476,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/health-bot/health-bot/main.bicep b/avm/res/health-bot/health-bot/main.bicep index eadc0903b3..73b357c7b9 100644 --- a/avm/res/health-bot/health-bot/main.bicep +++ b/avm/res/health-bot/health-bot/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.healthbot-healthbot.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -100,14 +111,10 @@ resource healthBot_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty( } resource healthBot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(healthBot.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(healthBot.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -149,6 +156,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/health-bot/health-bot/main.json b/avm/res/health-bot/health-bot/main.json index 731a1c61a5..cc92a85935 100644 --- a/avm/res/health-bot/health-bot/main.json +++ b/avm/res/health-bot/health-bot/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16112034149385088011" + "version": "0.29.47.4906", + "templateHash": "15586747651059655196" }, "name": "Azure Health Bots", "description": "This module deploys an Azure Health Bot.", @@ -58,6 +58,13 @@ "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": { @@ -179,6 +186,13 @@ } }, "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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -239,20 +253,20 @@ "healthBot_roleAssignments": { "copy": { "name": "healthBot_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.HealthBot/healthBots/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.HealthBot/healthBots', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.HealthBot/healthBots', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "healthBot" diff --git a/avm/res/health-bot/health-bot/tests/e2e/max/main.test.bicep b/avm/res/health-bot/health-bot/tests/e2e/max/main.test.bicep index bb00e40d5f..ddf9fbd314 100644 --- a/avm/res/health-bot/health-bot/tests/e2e/max/main.test.bicep +++ b/avm/res/health-bot/health-bot/tests/e2e/max/main.test.bicep @@ -58,11 +58,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '9d89b5ea-0d1f-41d8-9297-52529827d712' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/health-bot/health-bot/version.json b/avm/res/health-bot/health-bot/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/health-bot/health-bot/version.json +++ b/avm/res/health-bot/health-bot/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/healthcare-apis/workspace/README.md b/avm/res/healthcare-apis/workspace/README.md index 43422ece17..8923ba37b8 100644 --- a/avm/res/healthcare-apis/workspace/README.md +++ b/avm/res/healthcare-apis/workspace/README.md @@ -214,11 +214,13 @@ module workspace 'br/public:avm/res/healthcare-apis/workspace:' = { publicNetworkAccess: 'Enabled' roleAssignments: [ { + name: '6bfff821-2b18-4790-89fa-2849d86bc6be' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -374,11 +376,13 @@ module workspace 'br/public:avm/res/healthcare-apis/workspace:' = { "roleAssignments": { "value": [ { + "name": "6bfff821-2b18-4790-89fa-2849d86bc6be", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -604,6 +608,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -654,6 +659,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/healthcare-apis/workspace/dicomservice/main.json b/avm/res/healthcare-apis/workspace/dicomservice/main.json index 852a205ef4..b08d7fd0df 100644 --- a/avm/res/healthcare-apis/workspace/dicomservice/main.json +++ b/avm/res/healthcare-apis/workspace/dicomservice/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4586229415263092385" + "version": "0.29.47.4906", + "templateHash": "1751217677948929755" }, "name": "Healthcare API Workspace DICOM Services", "description": "This module deploys a Healthcare API Workspace DICOM Service.", diff --git a/avm/res/healthcare-apis/workspace/fhirservice/README.md b/avm/res/healthcare-apis/workspace/fhirservice/README.md index 87df130ff3..2c7c7ace88 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/README.md +++ b/avm/res/healthcare-apis/workspace/fhirservice/README.md @@ -486,6 +486,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -536,6 +537,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/healthcare-apis/workspace/fhirservice/main.bicep b/avm/res/healthcare-apis/workspace/fhirservice/main.bicep index 4a3bdf8169..2dde69b5f5 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/main.bicep +++ b/avm/res/healthcare-apis/workspace/fhirservice/main.bicep @@ -178,6 +178,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource workspace 'Microsoft.HealthcareApis/workspaces@2022-06-01' existing = { name: workspaceName } @@ -266,14 +277,10 @@ resource fhir_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05- ] resource fhir_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(fhir.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(fhir.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -329,6 +336,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/healthcare-apis/workspace/fhirservice/main.json b/avm/res/healthcare-apis/workspace/fhirservice/main.json index bbedf5ce01..97cb613498 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/main.json +++ b/avm/res/healthcare-apis/workspace/fhirservice/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3667449551488622874" + "version": "0.29.47.4906", + "templateHash": "1079296911965087473" }, "name": "Healthcare API Workspace FHIR Services", "description": "This module deploys a Healthcare API Workspace FHIR Service.", @@ -66,6 +66,13 @@ "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": { @@ -457,6 +464,13 @@ } }, "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())]", "exportConfiguration": { @@ -594,20 +608,20 @@ "fhir_roleAssignments": { "copy": { "name": "fhir_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.HealthcareApis/workspaces/{0}/fhirservices/{1}', parameters('workspaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.HealthcareApis/workspaces/fhirservices', parameters('workspaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.HealthcareApis/workspaces/fhirservices', parameters('workspaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "fhir" diff --git a/avm/res/healthcare-apis/workspace/iotconnector/fhirdestination/main.json b/avm/res/healthcare-apis/workspace/iotconnector/fhirdestination/main.json index 5ccae2089b..87612193cc 100644 --- a/avm/res/healthcare-apis/workspace/iotconnector/fhirdestination/main.json +++ b/avm/res/healthcare-apis/workspace/iotconnector/fhirdestination/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1919170406291290922" + "version": "0.29.47.4906", + "templateHash": "12659357611219228021" }, "name": "Healthcare API Workspace IoT Connector FHIR Destinations", "description": "This module deploys a Healthcare API Workspace IoT Connector FHIR Destination.", diff --git a/avm/res/healthcare-apis/workspace/iotconnector/main.json b/avm/res/healthcare-apis/workspace/iotconnector/main.json index 0efdc76541..8b9a9c6bd5 100644 --- a/avm/res/healthcare-apis/workspace/iotconnector/main.json +++ b/avm/res/healthcare-apis/workspace/iotconnector/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9468002846034681576" + "version": "0.29.47.4906", + "templateHash": "13346867265471851551" }, "name": "Healthcare API Workspace IoT Connectors", "description": "This module deploys a Healthcare API Workspace IoT Connector.", @@ -392,8 +392,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1919170406291290922" + "version": "0.29.47.4906", + "templateHash": "12659357611219228021" }, "name": "Healthcare API Workspace IoT Connector FHIR Destinations", "description": "This module deploys a Healthcare API Workspace IoT Connector FHIR Destination.", diff --git a/avm/res/healthcare-apis/workspace/main.bicep b/avm/res/healthcare-apis/workspace/main.bicep index 1d1062f9bc..4dbee8a189 100644 --- a/avm/res/healthcare-apis/workspace/main.bicep +++ b/avm/res/healthcare-apis/workspace/main.bicep @@ -91,6 +91,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.healthcareapis-workspace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -131,14 +142,10 @@ resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty( } resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -254,6 +261,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/healthcare-apis/workspace/main.json b/avm/res/healthcare-apis/workspace/main.json index 16f43122c7..e3d6da5a0d 100644 --- a/avm/res/healthcare-apis/workspace/main.json +++ b/avm/res/healthcare-apis/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9459505012519017576" + "version": "0.29.47.4906", + "templateHash": "16749021786732296216" }, "name": "Healthcare API Workspaces", "description": "This module deploys a Healthcare API Workspace.", @@ -43,6 +43,13 @@ "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": { @@ -181,6 +188,13 @@ } }, "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')]", "DICOM Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '58a3b984-7adf-4c20-983a-32417c86fbc8')]", @@ -246,20 +260,20 @@ "workspace_roleAssignments": { "copy": { "name": "workspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.HealthcareApis/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.HealthcareApis/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.HealthcareApis/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "workspace" @@ -368,8 +382,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3667449551488622874" + "version": "0.29.47.4906", + "templateHash": "1079296911965087473" }, "name": "Healthcare API Workspace FHIR Services", "description": "This module deploys a Healthcare API Workspace FHIR Service.", @@ -429,6 +443,13 @@ "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": { @@ -820,6 +841,13 @@ } }, "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())]", "exportConfiguration": { @@ -957,20 +985,20 @@ "fhir_roleAssignments": { "copy": { "name": "fhir_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.HealthcareApis/workspaces/{0}/fhirservices/{1}', parameters('workspaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.HealthcareApis/workspaces/fhirservices', parameters('workspaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.HealthcareApis/workspaces/fhirservices', parameters('workspaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "fhir" @@ -1088,8 +1116,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4586229415263092385" + "version": "0.29.47.4906", + "templateHash": "1751217677948929755" }, "name": "Healthcare API Workspace DICOM Services", "description": "This module deploys a Healthcare API Workspace DICOM Service.", @@ -1555,8 +1583,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9468002846034681576" + "version": "0.29.47.4906", + "templateHash": "13346867265471851551" }, "name": "Healthcare API Workspace IoT Connectors", "description": "This module deploys a Healthcare API Workspace IoT Connector.", @@ -1942,8 +1970,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1919170406291290922" + "version": "0.29.47.4906", + "templateHash": "12659357611219228021" }, "name": "Healthcare API Workspace IoT Connector FHIR Destinations", "description": "This module deploys a Healthcare API Workspace IoT Connector FHIR Destination.", diff --git a/avm/res/healthcare-apis/workspace/tests/e2e/max/main.test.bicep b/avm/res/healthcare-apis/workspace/tests/e2e/max/main.test.bicep index c9b023c20e..3eb5ae7c09 100644 --- a/avm/res/healthcare-apis/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/healthcare-apis/workspace/tests/e2e/max/main.test.bicep @@ -163,11 +163,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '6bfff821-2b18-4790-89fa-2849d86bc6be' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/healthcare-apis/workspace/version.json b/avm/res/healthcare-apis/workspace/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/healthcare-apis/workspace/version.json +++ b/avm/res/healthcare-apis/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/hybrid-compute/machine/README.md b/avm/res/hybrid-compute/machine/README.md index 8d2d8eb387..22aaf7d6a5 100644 --- a/avm/res/hybrid-compute/machine/README.md +++ b/avm/res/hybrid-compute/machine/README.md @@ -527,6 +527,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -577,6 +578,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/hybrid-compute/machine/extension/main.json b/avm/res/hybrid-compute/machine/extension/main.json index 4c7f7f20b5..01c3691774 100644 --- a/avm/res/hybrid-compute/machine/extension/main.json +++ b/avm/res/hybrid-compute/machine/extension/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16938870032761436213" + "version": "0.29.47.4906", + "templateHash": "12739944716751137956" }, "name": "Arc Machine Extensions", "description": "This module deploys a Arc Machine Extension. This module should be used as a standalone deployment after the Arc agent has connected to the Arc Machine resource.", diff --git a/avm/res/hybrid-compute/machine/main.bicep b/avm/res/hybrid-compute/machine/main.bicep index 7ceb726e2b..69f476dc28 100644 --- a/avm/res/hybrid-compute/machine/main.bicep +++ b/avm/res/hybrid-compute/machine/main.bicep @@ -117,6 +117,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { name: '46d3xbcp.res.hybridcompute-machine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -178,14 +189,10 @@ resource machine_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource machine_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(machine.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(machine.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -233,6 +240,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/hybrid-compute/machine/main.json b/avm/res/hybrid-compute/machine/main.json index a2151e8063..6cae863dae 100644 --- a/avm/res/hybrid-compute/machine/main.json +++ b/avm/res/hybrid-compute/machine/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6825923126291924605" + "version": "0.29.47.4906", + "templateHash": "3008449694375664417" }, "name": "Hybrid Compute Machines", "description": "This module deploys an Arc Machine for use with Arc Resource Bridge for Azure Stack HCI or VMware. In these scenarios, this resource module will be used in combination with another resource module to create the require Virtual Machine Instance extension resource on this Arc Machine resource. This module should not be used for other Arc-enabled server scenarios, where the Arc Machine resource is created automatically by the onboarding process.", @@ -66,6 +66,13 @@ "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": { @@ -246,6 +253,13 @@ } }, "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)))))]" + } + ], "linuxConfiguration": { "patchSettings": "[if(or(equals(parameters('patchMode'), 'AutomaticByPlatform'), equals(parameters('patchMode'), 'ImageDefault')), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode')), null())]" }, @@ -337,20 +351,20 @@ "machine_roleAssignments": { "copy": { "name": "machine_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.HybridCompute/machines/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.HybridCompute/machines', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.HybridCompute/machines', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "machine" diff --git a/avm/res/hybrid-compute/machine/version.json b/avm/res/hybrid-compute/machine/version.json index 0200aa0775..729ac87673 100644 --- a/avm/res/hybrid-compute/machine/version.json +++ b/avm/res/hybrid-compute/machine/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ - "./main.json" + "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/insights/action-group/README.md b/avm/res/insights/action-group/README.md index fe40b8a7f0..8cd17e4216 100644 --- a/avm/res/insights/action-group/README.md +++ b/avm/res/insights/action-group/README.md @@ -114,11 +114,13 @@ module actionGroup 'br/public:avm/res/insights/action-group:' = { location: 'global' roleAssignments: [ { + name: 'fc3ee4d9-d0c0-42c2-962f-082cf8d78882' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -185,11 +187,13 @@ module actionGroup 'br/public:avm/res/insights/action-group:' = { "roleAssignments": { "value": [ { + "name": "fc3ee4d9-d0c0-42c2-962f-082cf8d78882", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -427,6 +431,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -477,6 +482,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/action-group/main.bicep b/avm/res/insights/action-group/main.bicep index 5cb0184add..1477f8c79f 100644 --- a/avm/res/insights/action-group/main.bicep +++ b/avm/res/insights/action-group/main.bicep @@ -67,6 +67,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-actiongroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -107,14 +118,10 @@ resource actionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = { } resource actionGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(actionGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(actionGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -143,6 +150,9 @@ output location string = actionGroup.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/insights/action-group/main.json b/avm/res/insights/action-group/main.json index 8babc37cae..fb7b2e5eb3 100644 --- a/avm/res/insights/action-group/main.json +++ b/avm/res/insights/action-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15035714421773741398" + "version": "0.29.47.4906", + "templateHash": "3843866725906851395" }, "name": "Action Groups", "description": "This module deploys an Action Group.", @@ -18,6 +18,13 @@ "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": { @@ -199,6 +206,13 @@ } }, "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')]", @@ -252,20 +266,20 @@ "actionGroup_roleAssignments": { "copy": { "name": "actionGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/actionGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/actionGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/actionGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "actionGroup" diff --git a/avm/res/insights/action-group/tests/e2e/max/main.test.bicep b/avm/res/insights/action-group/tests/e2e/max/main.test.bicep index 3a06f69507..f601e54f96 100644 --- a/avm/res/insights/action-group/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/action-group/tests/e2e/max/main.test.bicep @@ -67,11 +67,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'fc3ee4d9-d0c0-42c2-962f-082cf8d78882' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/action-group/version.json b/avm/res/insights/action-group/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/action-group/version.json +++ b/avm/res/insights/action-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/activity-log-alert/README.md b/avm/res/insights/activity-log-alert/README.md index 95fff9ed23..f54c6eb1a2 100644 --- a/avm/res/insights/activity-log-alert/README.md +++ b/avm/res/insights/activity-log-alert/README.md @@ -196,11 +196,13 @@ module activityLogAlert 'br/public:avm/res/insights/activity-log-alert: location: 'global' roleAssignments: [ { + name: 'be96d7a9-6596-40c7-9acd-db6acd5cd41b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -287,11 +289,13 @@ module activityLogAlert 'br/public:avm/res/insights/activity-log-alert: "roleAssignments": { "value": [ { + "name": "be96d7a9-6596-40c7-9acd-db6acd5cd41b", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -567,6 +571,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -617,6 +622,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/activity-log-alert/main.bicep b/avm/res/insights/activity-log-alert/main.bicep index 58864bcd8c..1107cf6aed 100644 --- a/avm/res/insights/activity-log-alert/main.bicep +++ b/avm/res/insights/activity-log-alert/main.bicep @@ -55,6 +55,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-activitylogalert.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -92,14 +103,10 @@ resource activityLogAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' = { } resource activityLogAlert_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(activityLogAlert.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(activityLogAlert.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -128,6 +135,9 @@ output location string = activityLogAlert.location // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/insights/activity-log-alert/main.json b/avm/res/insights/activity-log-alert/main.json index 2f5f13a05c..725b8af89d 100644 --- a/avm/res/insights/activity-log-alert/main.json +++ b/avm/res/insights/activity-log-alert/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9400074646620662243" + "version": "0.29.47.4906", + "templateHash": "13226411448314617147" }, "name": "Activity Log Alerts", "description": "This module deploys an Activity Log Alert.", @@ -18,6 +18,13 @@ "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": { @@ -160,6 +167,11 @@ "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", "webhookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webhookProperties'), parameters('actions')[copyIndex('actionGroups')].webhookProperties, null())]" } + }, + { + "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": { @@ -212,20 +224,20 @@ "activityLogAlert_roleAssignments": { "copy": { "name": "activityLogAlert_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/activityLogAlerts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/activityLogAlerts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/activityLogAlerts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "activityLogAlert" diff --git a/avm/res/insights/activity-log-alert/tests/e2e/max/main.test.bicep b/avm/res/insights/activity-log-alert/tests/e2e/max/main.test.bicep index dd3bb2c84b..dd3120e2af 100644 --- a/avm/res/insights/activity-log-alert/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/activity-log-alert/tests/e2e/max/main.test.bicep @@ -92,11 +92,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'be96d7a9-6596-40c7-9acd-db6acd5cd41b' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/activity-log-alert/version.json b/avm/res/insights/activity-log-alert/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/insights/activity-log-alert/version.json +++ b/avm/res/insights/activity-log-alert/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/component/README.md b/avm/res/insights/component/README.md index 356dcd7811..eb2161a55c 100644 --- a/avm/res/insights/component/README.md +++ b/avm/res/insights/component/README.md @@ -122,11 +122,13 @@ module component 'br/public:avm/res/insights/component:' = { location: '' roleAssignments: [ { + name: '8aacced3-3fce-41bc-a416-959df1acec57' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -200,11 +202,13 @@ module component 'br/public:avm/res/insights/component:' = { "roleAssignments": { "value": [ { + "name": "8aacced3-3fce-41bc-a416-959df1acec57", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -659,6 +663,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -709,6 +714,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/component/linkedStorageAccounts/main.json b/avm/res/insights/component/linkedStorageAccounts/main.json index 174d82005e..a5d6e69bab 100644 --- a/avm/res/insights/component/linkedStorageAccounts/main.json +++ b/avm/res/insights/component/linkedStorageAccounts/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11213930862679606021" + "version": "0.29.47.4906", + "templateHash": "216781367921725873" }, "name": "Application Insights Linked Storage Account", "description": "This component deploys an Application Insights Linked Storage Account.", diff --git a/avm/res/insights/component/main.bicep b/avm/res/insights/component/main.bicep index 8b7676a005..a7a3c60dca 100644 --- a/avm/res/insights/component/main.bicep +++ b/avm/res/insights/component/main.bicep @@ -82,7 +82,7 @@ var 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 (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) @@ -90,8 +90,35 @@ var builtInRoleNames = { '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' + ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-component.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -138,14 +165,10 @@ module linkedStorageAccount 'linkedStorageAccounts/main.bicep' = if (!empty(link } resource appInsights_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(appInsights.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(appInsights.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -212,6 +235,9 @@ output connectionString string = appInsights.properties.ConnectionString // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/insights/component/main.json b/avm/res/insights/component/main.json index fc7402ee18..f7b9e3a933 100644 --- a/avm/res/insights/component/main.json +++ b/avm/res/insights/component/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1190253474397115720" + "version": "0.29.47.4906", + "templateHash": "707617228684994883" }, "name": "Application Insights", "description": "This component deploys an Application Insights instance.", @@ -18,6 +18,13 @@ "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": { @@ -343,12 +350,23 @@ } }, "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 (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", - "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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": { @@ -394,20 +412,20 @@ "appInsights_roleAssignments": { "copy": { "name": "appInsights_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/components', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -478,8 +496,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11213930862679606021" + "version": "0.29.47.4906", + "templateHash": "216781367921725873" }, "name": "Application Insights Linked Storage Account", "description": "This component deploys an Application Insights Linked Storage Account.", diff --git a/avm/res/insights/component/tests/e2e/max/main.test.bicep b/avm/res/insights/component/tests/e2e/max/main.test.bicep index 899534b33a..b157ba59e2 100644 --- a/avm/res/insights/component/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/component/tests/e2e/max/main.test.bicep @@ -86,11 +86,13 @@ module testDeployment '../../../main.bicep' = { ] roleAssignments: [ { + name: '8aacced3-3fce-41bc-a416-959df1acec57' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/component/version.json b/avm/res/insights/component/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/insights/component/version.json +++ b/avm/res/insights/component/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/data-collection-endpoint/README.md b/avm/res/insights/data-collection-endpoint/README.md index dccb01614e..5a7e92009f 100644 --- a/avm/res/insights/data-collection-endpoint/README.md +++ b/avm/res/insights/data-collection-endpoint/README.md @@ -17,7 +17,7 @@ This module deploys a Data Collection Endpoint. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Insights/dataCollectionEndpoints` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/dataCollectionEndpoints) | +| `Microsoft.Insights/dataCollectionEndpoints` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-11/dataCollectionEndpoints) | ## Usage examples @@ -105,11 +105,13 @@ module dataCollectionEndpoint 'br/public:avm/res/insights/data-collection-endpoi publicNetworkAccess: 'Enabled' roleAssignments: [ { + name: 'db496446-89ac-4d91-a189-71544de0150a' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -167,11 +169,13 @@ module dataCollectionEndpoint 'br/public:avm/res/insights/data-collection-endpoi "roleAssignments": { "value": [ { + "name": "db496446-89ac-4d91-a189-71544de0150a", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -405,6 +409,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -455,6 +460,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/data-collection-endpoint/main.bicep b/avm/res/insights/data-collection-endpoint/main.bicep index 876d09233e..6cb06c844f 100644 --- a/avm/res/insights/data-collection-endpoint/main.bicep +++ b/avm/res/insights/data-collection-endpoint/main.bicep @@ -55,6 +55,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // =============== // // Deployments // // =============== // @@ -103,14 +114,14 @@ resource dataCollectionEndpoint_lock 'Microsoft.Authorization/locks@2020-05-01' } resource dataCollectionEndpoint_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dataCollectionEndpoint.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + dataCollectionEndpoint.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -151,6 +162,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/insights/data-collection-endpoint/main.json b/avm/res/insights/data-collection-endpoint/main.json index c6efa6dee5..285f910d64 100644 --- a/avm/res/insights/data-collection-endpoint/main.json +++ b/avm/res/insights/data-collection-endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4485952202932766854" + "version": "0.29.47.4906", + "templateHash": "6503238538933862648" }, "name": "Data Collection Endpoints", "description": "This module deploys a Data Collection Endpoint.", @@ -43,6 +43,13 @@ "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": { @@ -176,6 +183,13 @@ } }, "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')]", @@ -236,20 +250,20 @@ "dataCollectionEndpoint_roleAssignments": { "copy": { "name": "dataCollectionEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/dataCollectionEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/dataCollectionEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/dataCollectionEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dataCollectionEndpoint" diff --git a/avm/res/insights/data-collection-endpoint/tests/e2e/max/main.test.bicep b/avm/res/insights/data-collection-endpoint/tests/e2e/max/main.test.bicep index 0a20a8aeea..20b770a8a4 100644 --- a/avm/res/insights/data-collection-endpoint/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/data-collection-endpoint/tests/e2e/max/main.test.bicep @@ -60,11 +60,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'db496446-89ac-4d91-a189-71544de0150a' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/data-collection-endpoint/version.json b/avm/res/insights/data-collection-endpoint/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/data-collection-endpoint/version.json +++ b/avm/res/insights/data-collection-endpoint/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/data-collection-rule/README.md b/avm/res/insights/data-collection-rule/README.md index 2cd6623d8e..ab796d654b 100644 --- a/avm/res/insights/data-collection-rule/README.md +++ b/avm/res/insights/data-collection-rule/README.md @@ -17,7 +17,7 @@ This module deploys a Data Collection Rule. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Insights/dataCollectionRules` | [2021-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-09-01-preview/dataCollectionRules) | +| `Microsoft.Insights/dataCollectionRules` | [2023-03-11](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-11/dataCollectionRules) | ## Usage examples @@ -27,18 +27,19 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/insights/data-collection-rule:`. -- [Collecting custom text logs with ingestion-time transformation](#example-1-collecting-custom-text-logs-with-ingestion-time-transformation) -- [Collecting custom text logs](#example-2-collecting-custom-text-logs) -- [Collecting IIS logs](#example-3-collecting-iis-logs) -- [Using only defaults](#example-4-using-only-defaults) -- [Collecting Linux-specific information](#example-5-collecting-linux-specific-information) -- [Using large parameter set](#example-6-using-large-parameter-set) -- [WAF-aligned](#example-7-waf-aligned) -- [Collecting Windows-specific information](#example-8-collecting-windows-specific-information) +- [Agent Settings](#example-1-agent-settings) +- [Collecting custom text logs with ingestion-time transformation](#example-2-collecting-custom-text-logs-with-ingestion-time-transformation) +- [Collecting custom text logs](#example-3-collecting-custom-text-logs) +- [Collecting IIS logs](#example-4-collecting-iis-logs) +- [Using only defaults](#example-5-using-only-defaults) +- [Collecting Linux-specific information](#example-6-collecting-linux-specific-information) +- [Using large parameter set](#example-7-using-large-parameter-set) +- [WAF-aligned](#example-8-waf-aligned) +- [Collecting Windows-specific information](#example-9-collecting-windows-specific-information) -### Example 1: _Collecting custom text logs with ingestion-time transformation_ +### Example 1: _Agent Settings_ -This instance deploys the module to setup collection of custom logs and ingestion-time transformation. +This instance deploys the module AMA (Azure Monitor Agent) Settings DCR.

@@ -50,82 +51,158 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableAdvanced_CL' - streams: [ - 'Custom-CustomTableAdvanced_CL' + dataCollectionRuleProperties: { + agentSettings: { + logs: [ + { + name: 'MaxDiskQuotaInMB' + value: '5000' + } ] - transformKql: 'source | extend LogFields = split(RawData, \',\') | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' } - ] - dataSources: { - logFiles: [ + description: 'Agent Settings' + kind: 'AgentSettings' + } + name: 'idcrags001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "dataCollectionRuleProperties": { + "value": { + "agentSettings": { + "logs": [ + { + "name": "MaxDiskQuotaInMB", + "value": "5000" + } + ] + }, + "description": "Agent Settings", + "kind": "AgentSettings" + } + }, + "name": { + "value": "idcrags001" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Collecting custom text logs with ingestion-time transformation_ + +This instance deploys the module to setup collection of custom logs and ingestion-time transformation. + + +

+ +via Bicep module + +```bicep +module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' = { + name: 'dataCollectionRuleDeployment' + params: { + // Required parameters + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsAdvanced\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableAdvanced_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableAdvanced_CL' streams: [ 'Custom-CustomTableAdvanced_CL' ] + transformKql: 'source | extend LogFields = split(RawData, \',\') | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] - } - name: 'idcrcusadv001' - // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \',,,\', for example: \'2023-01-25T20:15:05Z,ERROR,404,Page not found\'' - kind: 'Windows' - location: '' - streamDeclarations: { - 'Custom-CustomTableAdvanced_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'EventTime' - type: 'datetime' - } - { - name: 'EventLevel' - type: 'string' - } - { - name: 'EventCode' - type: 'int' - } - { - name: 'Message' - type: 'string' + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] } + ] + } + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \',,,\', for example: \'2023-01-25T20:15:05Z,ERROR,404,Page not found\'' + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + name: '' + workspaceResourceId: '' } ] } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } + name: 'idcrcusadv001' + // Non-required parameters + location: '' tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -148,100 +225,90 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableAdvanced_CL", - "streams": [ - "Custom-CustomTableAdvanced_CL" - ], - "transformKql": "source | extend LogFields = split(RawData, \",\") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsAdvanced\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableAdvanced_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableAdvanced_CL", "streams": [ "Custom-CustomTableAdvanced_CL" - ] + ], + "transformKql": "source | extend LogFields = split(RawData, \",\") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsAdvanced\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableAdvanced_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableAdvanced_CL" + ] + } + ] + }, + "description": "Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \",,,\", for example: \"2023-01-25T20:15:05Z,ERROR,404,Page not found\"", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableAdvanced_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventLevel", + "type": "string" + }, + { + "name": "EventCode", + "type": "int" + }, + { + "name": "Message", + "type": "string" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrcusadv001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \",,,\", for example: \"2023-01-25T20:15:05Z,ERROR,404,Page not found\"" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, - "streamDeclarations": { - "value": { - "Custom-CustomTableAdvanced_CL": { - "columns": [ - { - "name": "TimeGenerated", - "type": "datetime" - }, - { - "name": "EventTime", - "type": "datetime" - }, - { - "name": "EventLevel", - "type": "string" - }, - { - "name": "EventCode", - "type": "int" - }, - { - "name": "Message", - "type": "string" - }, - { - "name": "RawData", - "type": "string" - } - ] - } - } - }, "tags": { "value": { "hidden-title": "This is visible in the resource name", @@ -256,7 +323,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 2: _Collecting custom text logs_ +### Example 3: _Collecting custom text logs_ This instance deploys the module to setup collection of custom logs. @@ -270,66 +337,68 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableBasic_CL' - streams: [ - 'Custom-CustomTableBasic_CL' - ] - transformKql: 'source' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableBasic_CL' streams: [ 'Custom-CustomTableBasic_CL' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] - } - name: 'idcrcusbas001' - // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs without ingestion-time transformation.' - kind: 'Windows' - location: '' - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableBasic_CL' + ] } + ] + } + description: 'Collecting custom text logs without ingestion-time transformation.' + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + name: '' + workspaceResourceId: '' } ] } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } + name: 'idcrcusbas001' + // Non-required parameters + location: '' tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -352,84 +421,74 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableBasic_CL", - "streams": [ - "Custom-CustomTableBasic_CL" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsBasic\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableBasic_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableBasic_CL", "streams": [ "Custom-CustomTableBasic_CL" - ] + ], + "transformKql": "source" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsBasic\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableBasic_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableBasic_CL" + ] + } + ] + }, + "description": "Collecting custom text logs without ingestion-time transformation.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableBasic_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrcusbas001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs without ingestion-time transformation." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, - "streamDeclarations": { - "value": { - "Custom-CustomTableBasic_CL": { - "columns": [ - { - "name": "TimeGenerated", - "type": "datetime" - }, - { - "name": "RawData", - "type": "string" - } - ] - } - } - }, "tags": { "value": { "hidden-title": "This is visible in the resource name", @@ -444,7 +503,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 3: _Collecting IIS logs_ +### Example 4: _Collecting IIS logs_ This instance deploys the module to setup the collection of IIS logs. @@ -458,44 +517,46 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Microsoft-W3CIISLog' - streams: [ - 'Microsoft-W3CIISLog' - ] - transformKql: 'source' - } - ] - dataSources: { - iisLogs: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - logDirectories: [ - 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + destinations: [ + '' ] - name: 'iisLogsDataSource' + outputStream: 'Microsoft-W3CIISLog' streams: [ 'Microsoft-W3CIISLog' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' - } - ] + dataSources: { + iisLogs: [ + { + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + } + ] + } + description: 'Collecting IIS logs.' + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrcusiis001' // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting IIS logs.' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -519,58 +580,50 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Microsoft-W3CIISLog", - "streams": [ - "Microsoft-W3CIISLog" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "iisLogs": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "logDirectories": [ - "C:\\inetpub\\logs\\LogFiles\\W3SVC1" + "destinations": [ + "" ], - "name": "iisLogsDataSource", + "outputStream": "Microsoft-W3CIISLog", "streams": [ "Microsoft-W3CIISLog" - ] - } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "transformKql": "source" } - ] + ], + "dataSources": { + "iisLogs": [ + { + "logDirectories": [ + "C:\\inetpub\\logs\\LogFiles\\W3SVC1" + ], + "name": "iisLogsDataSource", + "streams": [ + "Microsoft-W3CIISLog" + ] + } + ] + }, + "description": "Collecting IIS logs.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrcusiis001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting IIS logs." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -588,7 +641,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 4: _Using only defaults_ +### Example 5: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -602,47 +655,49 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' } } @@ -661,57 +716,53 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" } @@ -722,7 +773,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 5: _Collecting Linux-specific information_ +### Example 6: _Collecting Linux-specific information_ This instance deploys the module to setup the collection of Linux-specific performance counters and Linux Syslog. @@ -736,157 +787,159 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Syslog' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - 'Logical Disk(*)\\% Free Inodes' - 'Logical Disk(*)\\% Free Space' - 'Logical Disk(*)\\% Used Inodes' - 'Logical Disk(*)\\% Used Space' - 'Logical Disk(*)\\Disk Read Bytes/sec' - 'Logical Disk(*)\\Disk Reads/sec' - 'Logical Disk(*)\\Disk Transfers/sec' - 'Logical Disk(*)\\Disk Write Bytes/sec' - 'Logical Disk(*)\\Disk Writes/sec' - 'Logical Disk(*)\\Free Megabytes' - 'Logical Disk(*)\\Logical Disk Bytes/sec' - 'Memory(*)\\% Available Memory' - 'Memory(*)\\% Available Swap Space' - 'Memory(*)\\% Used Memory' - 'Memory(*)\\% Used Swap Space' - 'Memory(*)\\Available MBytes Memory' - 'Memory(*)\\Available MBytes Swap' - 'Memory(*)\\Page Reads/sec' - 'Memory(*)\\Page Writes/sec' - 'Memory(*)\\Pages/sec' - 'Memory(*)\\Used MBytes Swap Space' - 'Memory(*)\\Used Memory MBytes' - 'Network(*)\\Total Bytes' - 'Network(*)\\Total Bytes Received' - 'Network(*)\\Total Bytes Transmitted' - 'Network(*)\\Total Collisions' - 'Network(*)\\Total Packets Received' - 'Network(*)\\Total Packets Transmitted' - 'Network(*)\\Total Rx Errors' - 'Network(*)\\Total Tx Errors' - 'Processor(*)\\% DPC Time' - 'Processor(*)\\% Idle Time' - 'Processor(*)\\% Interrupt Time' - 'Processor(*)\\% IO Wait Time' - 'Processor(*)\\% Nice Time' - 'Processor(*)\\% Privileged Time' - 'Processor(*)\\% Processor Time' - 'Processor(*)\\% User Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - syslog: [ - { - facilityNames: [ - 'auth' - 'authpriv' - ] - logLevels: [ - 'Alert' - 'Critical' - 'Debug' - 'Emergency' - 'Error' - 'Info' - 'Notice' - 'Warning' - ] - name: 'sysLogsDataSource-debugLevel' - streams: [ - 'Microsoft-Syslog' - ] - } { - facilityNames: [ - 'cron' - 'daemon' - 'kern' - 'local0' - 'mark' + destinations: [ + '' ] - logLevels: [ - 'Alert' - 'Critical' - 'Emergency' - 'Error' - 'Warning' - ] - name: 'sysLogsDataSource-warningLevel' - streams: [ - 'Microsoft-Syslog' - ] - } - { - facilityNames: [ - 'local1' - 'local2' - 'local3' - 'local4' - 'local5' - 'local6' - 'local7' - 'lpr' - 'mail' - 'news' - 'syslog' - ] - logLevels: [ - 'Alert' - 'Critical' - 'Emergency' - 'Error' - ] - name: 'sysLogsDataSource-errLevel' streams: [ 'Microsoft-Syslog' ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\% Used Swap Space' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\Used Memory MBytes' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Collisions' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Processor(*)\\% DPC Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% User Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + syslog: [ + { + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Debug' + 'Emergency' + 'Error' + 'Info' + 'Notice' + 'Warning' + ] + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'cron' + 'daemon' + 'kern' + 'local0' + 'mark' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + 'Warning' + ] + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + ] + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Linux-specific performance counters and Linux Syslog' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Linux' } name: 'idcrlin001' // Non-required parameters - description: 'Collecting Linux-specific performance counters and Linux Syslog' - kind: 'Linux' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -910,169 +963,163 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Syslog" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "Logical Disk(*)\\% Free Inodes", - "Logical Disk(*)\\% Free Space", - "Logical Disk(*)\\% Used Inodes", - "Logical Disk(*)\\% Used Space", - "Logical Disk(*)\\Disk Read Bytes/sec", - "Logical Disk(*)\\Disk Reads/sec", - "Logical Disk(*)\\Disk Transfers/sec", - "Logical Disk(*)\\Disk Write Bytes/sec", - "Logical Disk(*)\\Disk Writes/sec", - "Logical Disk(*)\\Free Megabytes", - "Logical Disk(*)\\Logical Disk Bytes/sec", - "Memory(*)\\% Available Memory", - "Memory(*)\\% Available Swap Space", - "Memory(*)\\% Used Memory", - "Memory(*)\\% Used Swap Space", - "Memory(*)\\Available MBytes Memory", - "Memory(*)\\Available MBytes Swap", - "Memory(*)\\Page Reads/sec", - "Memory(*)\\Page Writes/sec", - "Memory(*)\\Pages/sec", - "Memory(*)\\Used MBytes Swap Space", - "Memory(*)\\Used Memory MBytes", - "Network(*)\\Total Bytes", - "Network(*)\\Total Bytes Received", - "Network(*)\\Total Bytes Transmitted", - "Network(*)\\Total Collisions", - "Network(*)\\Total Packets Received", - "Network(*)\\Total Packets Transmitted", - "Network(*)\\Total Rx Errors", - "Network(*)\\Total Tx Errors", - "Processor(*)\\% DPC Time", - "Processor(*)\\% Idle Time", - "Processor(*)\\% Interrupt Time", - "Processor(*)\\% IO Wait Time", - "Processor(*)\\% Nice Time", - "Processor(*)\\% Privileged Time", - "Processor(*)\\% Processor Time", - "Processor(*)\\% User Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "syslog": [ - { - "facilityNames": [ - "auth", - "authpriv" - ], - "logLevels": [ - "Alert", - "Critical", - "Debug", - "Emergency", - "Error", - "Info", - "Notice", - "Warning" - ], - "name": "sysLogsDataSource-debugLevel", - "streams": [ - "Microsoft-Syslog" - ] }, { - "facilityNames": [ - "cron", - "daemon", - "kern", - "local0", - "mark" - ], - "logLevels": [ - "Alert", - "Critical", - "Emergency", - "Error", - "Warning" + "destinations": [ + "" ], - "name": "sysLogsDataSource-warningLevel", - "streams": [ - "Microsoft-Syslog" - ] - }, - { - "facilityNames": [ - "local1", - "local2", - "local3", - "local4", - "local5", - "local6", - "local7", - "lpr", - "mail", - "news", - "syslog" - ], - "logLevels": [ - "Alert", - "Critical", - "Emergency", - "Error" - ], - "name": "sysLogsDataSource-errLevel", "streams": [ "Microsoft-Syslog" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "Logical Disk(*)\\% Free Inodes", + "Logical Disk(*)\\% Free Space", + "Logical Disk(*)\\% Used Inodes", + "Logical Disk(*)\\% Used Space", + "Logical Disk(*)\\Disk Read Bytes/sec", + "Logical Disk(*)\\Disk Reads/sec", + "Logical Disk(*)\\Disk Transfers/sec", + "Logical Disk(*)\\Disk Write Bytes/sec", + "Logical Disk(*)\\Disk Writes/sec", + "Logical Disk(*)\\Free Megabytes", + "Logical Disk(*)\\Logical Disk Bytes/sec", + "Memory(*)\\% Available Memory", + "Memory(*)\\% Available Swap Space", + "Memory(*)\\% Used Memory", + "Memory(*)\\% Used Swap Space", + "Memory(*)\\Available MBytes Memory", + "Memory(*)\\Available MBytes Swap", + "Memory(*)\\Page Reads/sec", + "Memory(*)\\Page Writes/sec", + "Memory(*)\\Pages/sec", + "Memory(*)\\Used MBytes Swap Space", + "Memory(*)\\Used Memory MBytes", + "Network(*)\\Total Bytes", + "Network(*)\\Total Bytes Received", + "Network(*)\\Total Bytes Transmitted", + "Network(*)\\Total Collisions", + "Network(*)\\Total Packets Received", + "Network(*)\\Total Packets Transmitted", + "Network(*)\\Total Rx Errors", + "Network(*)\\Total Tx Errors", + "Processor(*)\\% DPC Time", + "Processor(*)\\% Idle Time", + "Processor(*)\\% Interrupt Time", + "Processor(*)\\% IO Wait Time", + "Processor(*)\\% Nice Time", + "Processor(*)\\% Privileged Time", + "Processor(*)\\% Processor Time", + "Processor(*)\\% User Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "syslog": [ + { + "facilityNames": [ + "auth", + "authpriv" + ], + "logLevels": [ + "Alert", + "Critical", + "Debug", + "Emergency", + "Error", + "Info", + "Notice", + "Warning" + ], + "name": "sysLogsDataSource-debugLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "cron", + "daemon", + "kern", + "local0", + "mark" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error", + "Warning" + ], + "name": "sysLogsDataSource-warningLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "local1", + "local2", + "local3", + "local4", + "local5", + "local6", + "local7", + "lpr", + "mail", + "news", + "syslog" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error" + ], + "name": "sysLogsDataSource-errLevel", + "streams": [ + "Microsoft-Syslog" + ] + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "description": "Collecting Linux-specific performance counters and Linux Syslog", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Linux" } }, "name": { "value": "idcrlin001" }, // Non-required parameters - "description": { - "value": "Collecting Linux-specific performance counters and Linux Syslog" - }, - "kind": { - "value": "Linux" - }, "location": { "value": "" }, @@ -1090,7 +1137,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 6: _Using large parameter set_ +### Example 7: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -1104,51 +1151,67 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - outputStream: 'Custom-CustomTableBasic_CL' - streams: [ - 'Custom-CustomTableBasic_CL' - ] - transformKql: 'source' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + dataCollectionEndpointResourceId: '' + dataFlows: [ { - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + '' ] - format: 'text' - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + outputStream: 'Custom-CustomTableBasic_CL' streams: [ 'Custom-CustomTableBasic_CL' ] + transformKql: 'source' } ] - } - destinations: { - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + dataSources: { + logFiles: [ + { + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableBasic_CL' + ] + } + ] + } + description: 'Collecting custom text logs without ingestion-time transformation.' + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] } - ] + } } name: 'idcrmax001' // Non-required parameters - dataCollectionEndpointId: '' - description: 'Collecting custom text logs without ingestion-time transformation.' - kind: 'Windows' location: '' lock: { kind: 'CanNotDelete' @@ -1156,11 +1219,13 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1171,20 +1236,6 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' } ] - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ - { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'RawData' - type: 'string' - } - ] - } - } tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -1207,65 +1258,71 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "outputStream": "Custom-CustomTableBasic_CL", - "streams": [ - "Custom-CustomTableBasic_CL" - ], - "transformKql": "source" - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "logFiles": [ + "dataCollectionEndpointResourceId": "", + "dataFlows": [ { - "filePatterns": [ - "C:\\TestLogsBasic\\TestLog*.log" + "destinations": [ + "" ], - "format": "text", - "name": "CustomTableBasic_CL", - "samplingFrequencyInSeconds": 60, - "settings": { - "text": { - "recordStartTimestampFormat": "ISO 8601" - } - }, + "outputStream": "Custom-CustomTableBasic_CL", "streams": [ "Custom-CustomTableBasic_CL" - ] + ], + "transformKql": "source" } - ] - } - }, - "destinations": { - "value": { - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" + ], + "dataSources": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsBasic\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableBasic_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableBasic_CL" + ] + } + ] + }, + "description": "Collecting custom text logs without ingestion-time transformation.", + "destinations": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows", + "streamDeclarations": { + "Custom-CustomTableBasic_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "RawData", + "type": "string" + } + ] } - ] + } } }, "name": { "value": "idcrmax001" }, // Non-required parameters - "dataCollectionEndpointId": { - "value": "" - }, - "description": { - "value": "Collecting custom text logs without ingestion-time transformation." - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1278,11 +1335,13 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1294,22 +1353,6 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 7: _WAF-aligned_ +### Example 8: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -1338,111 +1381,113 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Event' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Page Faults/sec' - '\\Memory\\Pages/sec' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Process(_Total)\\Handle Count' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Context Switches/sec' - '\\System\\Processes' - '\\System\\Processor Queue Length' - '\\System\\System Up Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' + destinations: [ + '' + ] streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Page Faults/sec' + '\\Memory\\Pages/sec' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrwaf001' // Non-required parameters - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -1466,123 +1511,117 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Event" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Page Faults/sec", - "\\Memory\\Pages/sec", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Process(_Total)\\Handle Count", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Context Switches/sec", - "\\System\\Processes", - "\\System\\Processor Queue Length", - "\\System\\System Up Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "windowsEventLogs": [ + }, { - "name": "eventLogsDataSource", + "destinations": [ + "" + ], "streams": [ "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "windowsEventLogs": [ + { + "name": "eventLogsDataSource", + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" + ] + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "description": "Collecting Windows-specific performance counters and Windows Event Logs", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrwaf001" }, // Non-required parameters - "description": { - "value": "Collecting Windows-specific performance counters and Windows Event Logs" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1600,7 +1639,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:

-### Example 8: _Collecting Windows-specific information_ +### Example 9: _Collecting Windows-specific information_ This instance deploys the module to setup the connection of Windows-specific performance counters and Windows Event Logs. @@ -1614,111 +1653,113 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' - ] - streams: [ - 'Microsoft-Event' - ] - } - ] - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + dataFlows: [ { - counterSpecifiers: [ - '\\LogicalDisk(_Total)\\% Disk Read Time' - '\\LogicalDisk(_Total)\\% Disk Time' - '\\LogicalDisk(_Total)\\% Disk Write Time' - '\\LogicalDisk(_Total)\\% Free Space' - '\\LogicalDisk(_Total)\\% Idle Time' - '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' - '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' - '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' - '\\LogicalDisk(_Total)\\Disk Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Reads/sec' - '\\LogicalDisk(_Total)\\Disk Transfers/sec' - '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' - '\\LogicalDisk(_Total)\\Disk Writes/sec' - '\\LogicalDisk(_Total)\\Free Megabytes' - '\\Memory\\% Committed Bytes In Use' - '\\Memory\\Available Bytes' - '\\Memory\\Cache Bytes' - '\\Memory\\Committed Bytes' - '\\Memory\\Page Faults/sec' - '\\Memory\\Pages/sec' - '\\Memory\\Pool Nonpaged Bytes' - '\\Memory\\Pool Paged Bytes' - '\\Network Interface(*)\\Bytes Received/sec' - '\\Network Interface(*)\\Bytes Sent/sec' - '\\Network Interface(*)\\Bytes Total/sec' - '\\Network Interface(*)\\Packets Outbound Errors' - '\\Network Interface(*)\\Packets Received Errors' - '\\Network Interface(*)\\Packets Received/sec' - '\\Network Interface(*)\\Packets Sent/sec' - '\\Network Interface(*)\\Packets/sec' - '\\Process(_Total)\\Handle Count' - '\\Process(_Total)\\Thread Count' - '\\Process(_Total)\\Working Set' - '\\Process(_Total)\\Working Set - Private' - '\\Processor Information(_Total)\\% Privileged Time' - '\\Processor Information(_Total)\\% Processor Time' - '\\Processor Information(_Total)\\% User Time' - '\\Processor Information(_Total)\\Processor Frequency' - '\\System\\Context Switches/sec' - '\\System\\Processes' - '\\System\\Processor Queue Length' - '\\System\\System Up Time' + destinations: [ + 'azureMonitorMetrics-default' ] - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' + destinations: [ + '' + ] streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - ] } ] - } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Page Faults/sec' + '\\Memory\\Pages/sec' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] } - logAnalytics: [ - { - name: '' - workspaceResourceId: '' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } - ] + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + kind: 'Windows' } name: 'idcrwin001' // Non-required parameters - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - kind: 'Windows' location: '' tags: { 'hidden-title': 'This is visible in the resource name' @@ -1742,123 +1783,117 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" - ], - "streams": [ - "Microsoft-Event" - ] - } - ] - }, - "dataSources": { + "dataCollectionRuleProperties": { "value": { - "performanceCounters": [ + "dataFlows": [ { - "counterSpecifiers": [ - "\\LogicalDisk(_Total)\\% Disk Read Time", - "\\LogicalDisk(_Total)\\% Disk Time", - "\\LogicalDisk(_Total)\\% Disk Write Time", - "\\LogicalDisk(_Total)\\% Free Space", - "\\LogicalDisk(_Total)\\% Idle Time", - "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", - "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", - "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", - "\\LogicalDisk(_Total)\\Disk Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Reads/sec", - "\\LogicalDisk(_Total)\\Disk Transfers/sec", - "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", - "\\LogicalDisk(_Total)\\Disk Writes/sec", - "\\LogicalDisk(_Total)\\Free Megabytes", - "\\Memory\\% Committed Bytes In Use", - "\\Memory\\Available Bytes", - "\\Memory\\Cache Bytes", - "\\Memory\\Committed Bytes", - "\\Memory\\Page Faults/sec", - "\\Memory\\Pages/sec", - "\\Memory\\Pool Nonpaged Bytes", - "\\Memory\\Pool Paged Bytes", - "\\Network Interface(*)\\Bytes Received/sec", - "\\Network Interface(*)\\Bytes Sent/sec", - "\\Network Interface(*)\\Bytes Total/sec", - "\\Network Interface(*)\\Packets Outbound Errors", - "\\Network Interface(*)\\Packets Received Errors", - "\\Network Interface(*)\\Packets Received/sec", - "\\Network Interface(*)\\Packets Sent/sec", - "\\Network Interface(*)\\Packets/sec", - "\\Process(_Total)\\Handle Count", - "\\Process(_Total)\\Thread Count", - "\\Process(_Total)\\Working Set", - "\\Process(_Total)\\Working Set - Private", - "\\Processor Information(_Total)\\% Privileged Time", - "\\Processor Information(_Total)\\% Processor Time", - "\\Processor Information(_Total)\\% User Time", - "\\Processor Information(_Total)\\Processor Frequency", - "\\System\\Context Switches/sec", - "\\System\\Processes", - "\\System\\Processor Queue Length", - "\\System\\System Up Time" + "destinations": [ + "azureMonitorMetrics-default" ], - "name": "perfCounterDataSource60", - "samplingFrequencyInSeconds": 60, "streams": [ "Microsoft-InsightsMetrics" ] - } - ], - "windowsEventLogs": [ + }, { - "name": "eventLogsDataSource", + "destinations": [ + "" + ], "streams": [ "Microsoft-Event" - ], - "xPathQueries": [ - "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", - "Security!*[System[(band(Keywords,13510798882111488))]]", - "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" ] } - ] - } - }, - "destinations": { - "value": { - "azureMonitorMetrics": { - "name": "azureMonitorMetrics-default" + ], + "dataSources": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "windowsEventLogs": [ + { + "name": "eventLogsDataSource", + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" + ] + } + ] }, - "logAnalytics": [ - { - "name": "", - "workspaceResourceId": "" - } - ] + "description": "Collecting Windows-specific performance counters and Windows Event Logs", + "destinations": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + }, + "kind": "Windows" } }, "name": { "value": "idcrwin001" }, // Non-required parameters - "description": { - "value": "Collecting Windows-specific performance counters and Windows Event Logs" - }, - "kind": { - "value": "Windows" - }, "location": { "value": "" }, @@ -1883,42 +1918,22 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:,,,", for example: "2023-01-25T20:15:05Z,ERROR,404,Page not found"' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableAdvanced_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source | extend LogFields = split(RawData, ",") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' - outputStream: 'Custom-CustomTableAdvanced_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): ",,,", for example: "2023-01-25T20:15:05Z,ERROR,404,Page not found"' + dataFlows: [ { - name: 'CustomTableAdvanced_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableAdvanced_CL' ] - filePatterns: [ - 'C:\\TestLogsAdvanced\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } - } - ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName + transformKql: 'source | extend LogFields = split(RawData, ",") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' + outputStream: 'Custom-CustomTableAdvanced_CL' } ] - } - streamDeclarations: { - 'Custom-CustomTableAdvanced_CL': { - columns: [ - { - name: 'TimeGenerated' - type: 'datetime' - } - { - name: 'EventTime' - type: 'datetime' - } + dataSources: { + logFiles: [ { - name: 'EventLevel' - type: 'string' - } - { - name: 'EventCode' - type: 'int' - } - { - name: 'Message' - type: 'string' + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep index cf1e2a9b3c..88cca8c40e 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep @@ -52,63 +52,65 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting custom text logs without ingestion-time transformation.' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableBasic_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Custom-CustomTableBasic_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs without ingestion-time transformation.' + dataFlows: [ { - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableBasic_CL' ] - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } - } - ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName + transformKql: 'source' + outputStream: 'Custom-CustomTableBasic_CL' } ] - } - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableBasic_CL' + ] + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep index fa08fbf64c..017061171a 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep @@ -52,42 +52,44 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting IIS logs.' - dataFlows: [ - { - streams: [ - 'Microsoft-W3CIISLog' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Microsoft-W3CIISLog' - } - ] - dataSources: { - iisLogs: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting IIS logs.' + dataFlows: [ { - name: 'iisLogsDataSource' streams: [ 'Microsoft-W3CIISLog' ] - logDirectories: [ - 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] + transformKql: 'source' + outputStream: 'Microsoft-W3CIISLog' } ] + dataSources: { + iisLogs: [ + { + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep index 8a9070c88e..6c6fd466da 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep @@ -42,45 +42,47 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + 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' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - 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' + destinations: [ + 'azureMonitorMetrics-default' ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - ] - kind: 'Windows' } } ] diff --git a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep index 7ffc209efb..0371f75541 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep @@ -51,155 +51,157 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Linux-specific performance counters and Linux Syslog' - dataSources: { - performanceCounters: [ - { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 - streams: [ - 'Microsoft-InsightsMetrics' - ] - counterSpecifiers: [ - 'Processor(*)\\% Processor Time' - 'Processor(*)\\% Idle Time' - 'Processor(*)\\% User Time' - 'Processor(*)\\% Nice Time' - 'Processor(*)\\% Privileged Time' - 'Processor(*)\\% IO Wait Time' - 'Processor(*)\\% Interrupt Time' - 'Processor(*)\\% DPC Time' - 'Memory(*)\\Available MBytes Memory' - 'Memory(*)\\% Available Memory' - 'Memory(*)\\Used Memory MBytes' - 'Memory(*)\\% Used Memory' - 'Memory(*)\\Pages/sec' - 'Memory(*)\\Page Reads/sec' - 'Memory(*)\\Page Writes/sec' - 'Memory(*)\\Available MBytes Swap' - 'Memory(*)\\% Available Swap Space' - 'Memory(*)\\Used MBytes Swap Space' - 'Memory(*)\\% Used Swap Space' - 'Logical Disk(*)\\% Free Inodes' - 'Logical Disk(*)\\% Used Inodes' - 'Logical Disk(*)\\Free Megabytes' - 'Logical Disk(*)\\% Free Space' - 'Logical Disk(*)\\% Used Space' - 'Logical Disk(*)\\Logical Disk Bytes/sec' - 'Logical Disk(*)\\Disk Read Bytes/sec' - 'Logical Disk(*)\\Disk Write Bytes/sec' - 'Logical Disk(*)\\Disk Transfers/sec' - 'Logical Disk(*)\\Disk Reads/sec' - 'Logical Disk(*)\\Disk Writes/sec' - 'Network(*)\\Total Bytes Transmitted' - 'Network(*)\\Total Bytes Received' - 'Network(*)\\Total Bytes' - 'Network(*)\\Total Packets Transmitted' - 'Network(*)\\Total Packets Received' - 'Network(*)\\Total Rx Errors' - 'Network(*)\\Total Tx Errors' - 'Network(*)\\Total Collisions' - ] - } - ] - syslog: [ - { - name: 'sysLogsDataSource-debugLevel' - streams: [ - 'Microsoft-Syslog' - ] - facilityNames: [ - 'auth' - 'authpriv' - ] - logLevels: [ - 'Debug' - 'Info' - 'Notice' - 'Warning' - 'Error' - 'Critical' - 'Alert' - 'Emergency' - ] + dataCollectionRuleProperties: { + kind: 'Linux' + description: 'Collecting Linux-specific performance counters and Linux Syslog' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% User Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% DPC Time' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\Used Memory MBytes' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\% Used Swap Space' + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Network(*)\\Total Collisions' + ] + } + ] + syslog: [ + { + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Debug' + 'Info' + 'Notice' + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'cron' + 'daemon' + 'mark' + 'kern' + 'local0' + ] + logLevels: [ + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'sysLogsDataSource-warningLevel' streams: [ - 'Microsoft-Syslog' - ] - facilityNames: [ - 'cron' - 'daemon' - 'mark' - 'kern' - 'local0' + 'Microsoft-InsightsMetrics' ] - logLevels: [ - 'Warning' - 'Error' - 'Critical' - 'Alert' - 'Emergency' + destinations: [ + 'azureMonitorMetrics-default' ] } { - name: 'sysLogsDataSource-errLevel' streams: [ 'Microsoft-Syslog' ] - facilityNames: [ - 'local1' - 'local2' - 'local3' - 'local4' - 'local5' - 'local6' - 'local7' - 'lpr' - 'mail' - 'news' - 'syslog' - ] - logLevels: [ - 'Error' - 'Critical' - 'Alert' - 'Emergency' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Syslog' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Linux' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep index cf81a64bfc..27ac14b5c4 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep @@ -53,74 +53,78 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - dataCollectionEndpointId: nestedDependencies.outputs.dataCollectionEndpointResourceId - description: 'Collecting custom text logs without ingestion-time transformation.' - dataFlows: [ - { - streams: [ - 'Custom-CustomTableBasic_CL' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - transformKql: 'source' - outputStream: 'Custom-CustomTableBasic_CL' - } - ] - dataSources: { - logFiles: [ + dataCollectionRuleProperties: { + kind: 'Windows' + dataCollectionEndpointResourceId: nestedDependencies.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs without ingestion-time transformation.' + dataFlows: [ { - name: 'CustomTableBasic_CL' - samplingFrequencyInSeconds: 60 streams: [ 'Custom-CustomTableBasic_CL' ] - filePatterns: [ - 'C:\\TestLogsBasic\\TestLog*.log' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] - format: 'text' - settings: { - text: { - recordStartTimestampFormat: 'ISO 8601' - } - } + transformKql: 'source' + outputStream: 'Custom-CustomTableBasic_CL' } ] - } - destinations: { - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - streamDeclarations: { - 'Custom-CustomTableBasic_CL': { - columns: [ + dataSources: { + logFiles: [ { - name: 'TimeGenerated' - type: 'datetime' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableBasic_CL' + ] + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } } + ] + } + destinations: { + logAnalytics: [ { - name: 'RawData' - type: 'string' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName } ] } + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } } - kind: 'Windows' lock: { kind: 'CanNotDelete' name: 'myCustomLockName' } roleAssignments: [ { + name: '89a4d6fa-defb-4099-9196-173d94b91d67' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep index c1b93f95b5..797344b7e1 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep @@ -52,109 +52,111 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + 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' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - 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' + destinations: [ + 'azureMonitorMetrics-default' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Event' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep index d5e29bfc40..7c58e5ede8 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep @@ -51,109 +51,111 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation - description: 'Collecting Windows-specific performance counters and Windows Event Logs' - dataSources: { - performanceCounters: [ + dataCollectionRuleProperties: { + kind: 'Windows' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + 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' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + name: nestedDependencies.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ { - name: 'perfCounterDataSource60' - samplingFrequencyInSeconds: 60 streams: [ 'Microsoft-InsightsMetrics' ] - 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' + destinations: [ + 'azureMonitorMetrics-default' ] } - ] - windowsEventLogs: [ { - name: 'eventLogsDataSource' streams: [ 'Microsoft-Event' ] - xPathQueries: [ - 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' - 'Security!*[System[(band(Keywords,13510798882111488))]]' - 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + destinations: [ + nestedDependencies.outputs.logAnalyticsWorkspaceName ] } ] } - destinations: { - azureMonitorMetrics: { - name: 'azureMonitorMetrics-default' - } - logAnalytics: [ - { - workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId - name: nestedDependencies.outputs.logAnalyticsWorkspaceName - } - ] - } - dataFlows: [ - { - streams: [ - 'Microsoft-InsightsMetrics' - ] - destinations: [ - 'azureMonitorMetrics-default' - ] - } - { - streams: [ - 'Microsoft-Event' - ] - destinations: [ - nestedDependencies.outputs.logAnalyticsWorkspaceName - ] - } - ] - kind: 'Windows' tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/version.json b/avm/res/insights/data-collection-rule/version.json index 83083db694..c177b1bb58 100644 --- a/avm/res/insights/data-collection-rule/version.json +++ b/avm/res/insights/data-collection-rule/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/metric-alert/README.md b/avm/res/insights/metric-alert/README.md index 573b4301db..7b03967bda 100644 --- a/avm/res/insights/metric-alert/README.md +++ b/avm/res/insights/metric-alert/README.md @@ -44,22 +44,26 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { name: 'metricAlertDeployment' params: { // Required parameters - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } - ] + criteria: { + allof: [ + { + criterionType: 'StaticThresholdCriterion' + dimensions: [] + metricName: 'Percentage CPU' + name: '1st criterion' + operator: 'GreaterThan' + threshold: 80 + timeAggregation: 'Average' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } name: 'imamin001' // Non-required parameters location: 'Global' - targetResourceRegion: 'westeurope' - targetResourceType: 'microsoft.compute/virtualmachines' + scopes: [ + '' + ] } } ``` @@ -77,18 +81,21 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters - "criterias": { - "value": [ - { - "criterionType": "StaticThresholdCriterion", - "metricName": "Percentage CPU", - "metricNamespace": "microsoft.compute/virtualmachines", - "name": "HighCPU", - "operator": "GreaterThan", - "threshold": "90", - "timeAggregation": "Average" - } - ] + "criteria": { + "value": { + "allof": [ + { + "criterionType": "StaticThresholdCriterion", + "dimensions": [], + "metricName": "Percentage CPU", + "name": "1st criterion", + "operator": "GreaterThan", + "threshold": 80, + "timeAggregation": "Average" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + } }, "name": { "value": "imamin001" @@ -97,11 +104,10 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "location": { "value": "Global" }, - "targetResourceRegion": { - "value": "westeurope" - }, - "targetResourceType": { - "value": "microsoft.compute/virtualmachines" + "scopes": { + "value": [ + "" + ] } } } @@ -124,31 +130,35 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { name: 'metricAlertDeployment' params: { // Required parameters - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } - ] + criteria: { + allof: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } name: 'imamax001' // Non-required parameters actions: [ '' ] - alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' location: 'Global' roleAssignments: [ { + name: '3ab52119-85d9-4374-a454-2410b84f19f9' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -184,18 +194,21 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters - "criterias": { - "value": [ - { - "criterionType": "StaticThresholdCriterion", - "metricName": "Percentage CPU", - "metricNamespace": "microsoft.compute/virtualmachines", - "name": "HighCPU", - "operator": "GreaterThan", - "threshold": "90", - "timeAggregation": "Average" - } - ] + "criteria": { + "value": { + "allof": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + } }, "name": { "value": "imamax001" @@ -206,20 +219,19 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "" ] }, - "alertCriteriaType": { - "value": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" - }, "location": { "value": "Global" }, "roleAssignments": { "value": [ { + "name": "3ab52119-85d9-4374-a454-2410b84f19f9", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -268,32 +280,29 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { name: 'metricAlertDeployment' params: { // Required parameters - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } - ] + criteria: { + componentResourceId: '' + failedLocationCount: 3 + 'odata.type': 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' + webTestResourceId: '' + } name: 'imawaf001' // Non-required parameters actions: [ '' ] - alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' - location: 'Global' + evaluationFrequency: 'PT5M' + location: 'global' + scopes: [ + '' + '' + ] tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - targetResourceRegion: 'westeurope' - targetResourceType: 'microsoft.compute/virtualmachines' - windowSize: 'PT15M' + windowSize: 'PT5M' } } ``` @@ -311,18 +320,13 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters - "criterias": { - "value": [ - { - "criterionType": "StaticThresholdCriterion", - "metricName": "Percentage CPU", - "metricNamespace": "microsoft.compute/virtualmachines", - "name": "HighCPU", - "operator": "GreaterThan", - "threshold": "90", - "timeAggregation": "Average" - } - ] + "criteria": { + "value": { + "componentResourceId": "", + "failedLocationCount": 3, + "odata.type": "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria", + "webTestResourceId": "" + } }, "name": { "value": "imawaf001" @@ -333,11 +337,17 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "" ] }, - "alertCriteriaType": { - "value": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + "evaluationFrequency": { + "value": "PT5M" }, "location": { - "value": "Global" + "value": "global" + }, + "scopes": { + "value": [ + "", + "" + ] }, "tags": { "value": { @@ -346,14 +356,8 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { "Role": "DeploymentValidation" } }, - "targetResourceRegion": { - "value": "westeurope" - }, - "targetResourceType": { - "value": "microsoft.compute/virtualmachines" - }, "windowSize": { - "value": "PT15M" + "value": "PT5M" } } } @@ -369,7 +373,7 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { | Parameter | Type | Description | | :-- | :-- | :-- | -| [`criterias`](#parameter-criterias) | array | Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects. When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory. It is not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. The alert must be deleted and recreated. | +| [`criteria`](#parameter-criteria) | object | Maps to the 'odata.type' field. Specifies the type of the alert criteria. | | [`name`](#parameter-name) | string | The name of the alert. | **Conditional parameters** @@ -384,7 +388,6 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`actions`](#parameter-actions) | array | The list of actions to take when alert triggers. | -| [`alertCriteriaType`](#parameter-alertcriteriatype) | string | Maps to the 'odata.type' field. Specifies the type of the alert criteria. | | [`alertDescription`](#parameter-alertdescription) | string | Description of the alert. | | [`autoMitigate`](#parameter-automitigate) | bool | The flag that indicates whether the alert should be auto resolved or not. | | [`enabled`](#parameter-enabled) | bool | Indicates whether this alert is enabled. | @@ -397,12 +400,12 @@ module metricAlert 'br/public:avm/res/insights/metric-alert:' = { | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`windowSize`](#parameter-windowsize) | string | the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold. | -### Parameter: `criterias` +### Parameter: `criteria` -Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects. When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory. It is not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. The alert must be deleted and recreated. +Maps to the 'odata.type' field. Specifies the type of the alert criteria. - Required: Yes -- Type: array +- Type: object ### Parameter: `name` @@ -417,7 +420,6 @@ The region of the target resource(s) on which the alert is created/updated. Requ - Required: No - Type: string -- Default: `''` ### Parameter: `targetResourceType` @@ -425,7 +427,6 @@ The resource type of the target resource(s) on which the alert is created/update - Required: No - Type: string -- Default: `''` ### Parameter: `actions` @@ -435,22 +436,6 @@ The list of actions to take when alert triggers. - Type: array - Default: `[]` -### Parameter: `alertCriteriaType` - -Maps to the 'odata.type' field. Specifies the type of the alert criteria. - -- Required: No -- Type: string -- Default: `'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'` -- Allowed: - ```Bicep - [ - 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' - 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' - 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' - ] - ``` - ### Parameter: `alertDescription` Description of the alert. @@ -531,6 +516,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -581,6 +567,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/metric-alert/main.bicep b/avm/res/insights/metric-alert/main.bicep index c183deca20..bd94d4d489 100644 --- a/avm/res/insights/metric-alert/main.bicep +++ b/avm/res/insights/metric-alert/main.bicep @@ -53,10 +53,10 @@ param scopes array = [ ] @description('Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') -param targetResourceType string = '' +param targetResourceType string? @description('Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') -param targetResourceRegion string = '' +param targetResourceRegion string? @description('Optional. The flag that indicates whether the alert should be auto resolved or not.') param autoMitigate bool = true @@ -64,16 +64,8 @@ param autoMitigate bool = true @description('Optional. The list of actions to take when alert triggers.') param actions array = [] -@description('Optional. Maps to the \'odata.type\' field. Specifies the type of the alert criteria.') -@allowed([ - 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' - 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' - 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' -]) -param alertCriteriaType string = 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' - -@description('Required. Criterias to trigger the alert. Array of \'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria\' or \'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria\' objects. When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory. It is not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. The alert must be deleted and recreated.') -param criterias array +@description('Required. Maps to the \'odata.type\' field. Specifies the type of the alert criteria.') +param criteria alertType @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType @@ -86,8 +78,8 @@ param enableTelemetry bool = true var actionGroups = [ for action in actions: { - actionGroupId: contains(action, 'actionGroupId') ? action.actionGroupId : action - webHookProperties: contains(action, 'webHookProperties') ? action.webHookProperties : null + actionGroupId: action.?actionGroupId ?? action + webHookProperties: action.?webHookProperties } ] @@ -105,6 +97,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-metricalert.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -137,24 +140,25 @@ resource metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = { windowSize: windowSize targetResourceType: targetResourceType targetResourceRegion: targetResourceRegion - criteria: { - 'odata.type': any(alertCriteriaType) - allOf: criterias - } + criteria: union( + { + 'odata.type': criteria['odata.type'] + }, + (contains(criteria, 'allof') ? { allof: criteria.allof } : {}), + (contains(criteria, 'componentResourceId') ? { componentId: criteria.componentResourceId } : {}), + (contains(criteria, 'failedLocationCount') ? { failedLocationCount: criteria.failedLocationCount } : {}), + (contains(criteria, 'webTestResourceId') ? { webTestId: criteria.webTestResourceId } : {}) + ) autoMitigate: autoMitigate actions: actionGroups } } resource metricAlert_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(metricAlert.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(metricAlert.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -182,6 +186,9 @@ output location string = metricAlert.location // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -203,3 +210,20 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +@discriminator('odata.type') +type alertType = alertWebtestType | alertResourceType | alertMultiResourceType +type alertResourceType = { + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + allof: array +} +type alertMultiResourceType = { + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + allof: array +} +type alertWebtestType = { + 'odata.type': 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' + componentResourceId: string + failedLocationCount: int + webTestResourceId: string +} diff --git a/avm/res/insights/metric-alert/main.json b/avm/res/insights/metric-alert/main.json index d23695a671..71e0cbcc63 100644 --- a/avm/res/insights/metric-alert/main.json +++ b/avm/res/insights/metric-alert/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12888844635682396371" + "version": "0.29.47.4906", + "templateHash": "5240734696906112690" }, "name": "Metric Alerts", "description": "This module deploys a Metric Alert.", @@ -18,6 +18,13 @@ "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": { @@ -78,6 +85,71 @@ } }, "nullable": true + }, + "alertType": { + "type": "object", + "discriminator": { + "propertyName": "odata.type", + "mapping": { + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria": { + "$ref": "#/definitions/alertWebtestType" + }, + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria": { + "$ref": "#/definitions/alertResourceType" + }, + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria": { + "$ref": "#/definitions/alertMultiResourceType" + } + } + } + }, + "alertResourceType": { + "type": "object", + "properties": { + "odata.type": { + "type": "string", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + ] + }, + "allof": { + "type": "array" + } + } + }, + "alertMultiResourceType": { + "type": "object", + "properties": { + "odata.type": { + "type": "string", + "allowedValues": [ + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + ] + }, + "allof": { + "type": "array" + } + } + }, + "alertWebtestType": { + "type": "object", + "properties": { + "odata.type": { + "type": "string", + "allowedValues": [ + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ] + }, + "componentResourceId": { + "type": "string" + }, + "failedLocationCount": { + "type": "int" + }, + "webTestResourceId": { + "type": "string" + } + } } }, "parameters": { @@ -164,14 +236,14 @@ }, "targetResourceType": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." } }, "targetResourceRegion": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." } @@ -190,22 +262,10 @@ "description": "Optional. The list of actions to take when alert triggers." } }, - "alertCriteriaType": { - "type": "string", - "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", - "allowedValues": [ - "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", - "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", - "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" - ], + "criteria": { + "$ref": "#/definitions/alertType", "metadata": { - "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." - } - }, - "criterias": { - "type": "array", - "metadata": { - "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects. When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory. It is not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. The alert must be deleted and recreated." + "description": "Required. Maps to the 'odata.type' field. Specifies the type of the alert criteria." } }, "roleAssignments": { @@ -235,9 +295,14 @@ "name": "actionGroups", "count": "[length(parameters('actions'))]", "input": { - "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", - "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + "actionGroupId": "[coalesce(tryGet(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[tryGet(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties')]" } + }, + { + "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": { @@ -284,10 +349,7 @@ "windowSize": "[parameters('windowSize')]", "targetResourceType": "[parameters('targetResourceType')]", "targetResourceRegion": "[parameters('targetResourceRegion')]", - "criteria": { - "odata.type": "[parameters('alertCriteriaType')]", - "allOf": "[parameters('criterias')]" - }, + "criteria": "[union(createObject('odata.type', parameters('criteria')['odata.type']), if(contains(parameters('criteria'), 'allof'), createObject('allof', parameters('criteria').allof), createObject()), if(contains(parameters('criteria'), 'componentResourceId'), createObject('componentId', parameters('criteria').componentResourceId), createObject()), if(contains(parameters('criteria'), 'failedLocationCount'), createObject('failedLocationCount', parameters('criteria').failedLocationCount), createObject()), if(contains(parameters('criteria'), 'webTestResourceId'), createObject('webTestId', parameters('criteria').webTestResourceId), createObject()))]", "autoMitigate": "[parameters('autoMitigate')]", "actions": "[variables('actionGroups')]" } @@ -295,20 +357,20 @@ "metricAlert_roleAssignments": { "copy": { "name": "metricAlert_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/metricAlerts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "metricAlert" diff --git a/avm/res/insights/metric-alert/tests/e2e/defaults/dependencies.bicep b/avm/res/insights/metric-alert/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..27a18a8ec1 --- /dev/null +++ b/avm/res/insights/metric-alert/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,78 @@ +@description('Required. Location to deploy resources to.') +param resourceLocation string + +@description('Required. The name of the Virtual Machine to create.') +param virtualMachineName string + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +module vm 'br/public:avm/res/compute/virtual-machine:0.5.3' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${virtualMachineName}' + params: { + location: resourceLocation + name: virtualMachineName + adminUsername: 'localAdminUser' + encryptionAtHost: false + imageReference: { + publisher: 'MicrosoftWindowsServer' + offer: 'WindowsServer' + sku: '2022-datacenter-azure-edition' + version: 'latest' + } + zone: 0 + nicConfigurations: [ + { + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: virtualNetwork.properties.subnets[0].id + } + ] + nicSuffix: '-nic-01' + } + ] + osDisk: { + diskSizeGB: 128 + caching: 'ReadWrite' + managedDisk: { + storageAccountType: 'Premium_LRS' + } + } + osType: 'Windows' + vmSize: 'Standard_DS2_v2' + adminPassword: password + } +} + +@description('The resource ID of the created Virtual Machine.') +output virtualMachineResourceId string = vm.outputs.resourceId diff --git a/avm/res/insights/metric-alert/tests/e2e/defaults/main.test.bicep b/avm/res/insights/metric-alert/tests/e2e/defaults/main.test.bicep index bf6a693cc3..d3ed8b3f3c 100644 --- a/avm/res/insights/metric-alert/tests/e2e/defaults/main.test.bicep +++ b/avm/res/insights/metric-alert/tests/e2e/defaults/main.test.bicep @@ -31,6 +31,16 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { location: resourceLocation } +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + resourceLocation: resourceLocation + virtualMachineName: '${namePrefix}${serviceShort}001' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + // ============== // // Test Execution // // ============== // @@ -43,19 +53,23 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: 'Global' - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } + criteria: { + allof: [ + { + name: '1st criterion' + metricName: 'Percentage CPU' + dimensions: [] + operator: 'GreaterThan' + threshold: 80 + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + scopes: [ + nestedDependencies.outputs.virtualMachineResourceId ] - targetResourceRegion: 'westeurope' - targetResourceType: 'microsoft.compute/virtualmachines' } } ] diff --git a/avm/res/insights/metric-alert/tests/e2e/max/main.test.bicep b/avm/res/insights/metric-alert/tests/e2e/max/main.test.bicep index 2d4de682aa..0ace1f32ff 100644 --- a/avm/res/insights/metric-alert/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/metric-alert/tests/e2e/max/main.test.bicep @@ -53,28 +53,32 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: 'Global' - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } - ] + criteria: { + allof: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } actions: [ nestedDependencies.outputs.actionGroupResourceId ] - alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' roleAssignments: [ { + name: '3ab52119-85d9-4374-a454-2410b84f19f9' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep index b332d7fdc1..3b74e92089 100644 --- a/avm/res/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/insights/metric-alert/tests/e2e/waf-aligned/dependencies.bicep @@ -1,6 +1,12 @@ @description('Required. The name of the Action Group to create.') param actionGroupName string +@description('Required. The name of the Application Insights to create.') +param appInsightsName string + +@description('Required. Location to deploy resources to.') +param resourceLocation string + resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { name: actionGroupName location: 'global' @@ -11,5 +17,63 @@ resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { } } +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightsName + location: resourceLocation + kind: 'web' + properties: { + Application_Type: 'web' + } +} + +resource pingTest 'Microsoft.Insights/webtests@2022-06-15' = { + name: 'PingTest-${toLower(appInsightsName)}' + location: resourceLocation + properties: { + Name: 'PingTest-${toLower(appInsightsName)}' + Description: 'Basic ping test' + Enabled: true + Frequency: 300 + Timeout: 120 + Kind: 'standard' + RetryEnabled: true + Locations: [ + { + Id: 'us-il-ch1-azr' + } + { + Id: 'latam-br-gru-edge' + } + { + Id: 'us-fl-mia-edge' + } + { + Id: 'apac-jp-kaw-edge' + } + { + Id: 'emea-fr-pra-edge' + } + ] + ValidationRules: { + ExpectedHttpStatusCode: 200 + IgnoreHttpStatusCode: false + SSLCheck: false + } + Request: { + RequestUrl: 'https://www.microsoft.com' + HttpVerb: 'GET' + ParseDependentRequests: false + } + SyntheticMonitorId: 'PingTest-${toLower(appInsightsName)}' + } + tags: { + 'hidden-link:${appInsights.id}': 'Resource' + } +} + @description('The resource ID of the created Action Group.') output actionGroupResourceId string = actionGroup.id +@description('The resource ID of the created Application Insights.') +output appInsightsResourceId string = appInsights.id +@description('The resource ID of the webtest.') +output pingTestResourceId string = pingTest.id diff --git a/avm/res/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep index 3c6db678cb..f656e6f105 100644 --- a/avm/res/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/metric-alert/tests/e2e/waf-aligned/main.test.bicep @@ -36,6 +36,8 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' params: { actionGroupName: 'dep-${namePrefix}-ag-${serviceShort}' + appInsightsName: 'dep-${namePrefix}-app-${serviceShort}' + resourceLocation: resourceLocation } } @@ -50,25 +52,22 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: 'Global' - criterias: [ - { - criterionType: 'StaticThresholdCriterion' - metricName: 'Percentage CPU' - metricNamespace: 'microsoft.compute/virtualmachines' - name: 'HighCPU' - operator: 'GreaterThan' - threshold: '90' - timeAggregation: 'Average' - } + location: 'global' + scopes: [ + nestedDependencies.outputs.pingTestResourceId + nestedDependencies.outputs.appInsightsResourceId ] + criteria: { + componentResourceId: nestedDependencies.outputs.appInsightsResourceId + failedLocationCount: 3 + 'odata.type': 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' + webTestResourceId: nestedDependencies.outputs.pingTestResourceId + } actions: [ nestedDependencies.outputs.actionGroupResourceId ] - alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' - targetResourceRegion: 'westeurope' - targetResourceType: 'microsoft.compute/virtualmachines' - windowSize: 'PT15M' + windowSize: 'PT5M' + evaluationFrequency: 'PT5M' tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/insights/metric-alert/version.json b/avm/res/insights/metric-alert/version.json index 83083db694..9481fea58e 100644 --- a/avm/res/insights/metric-alert/version.json +++ b/avm/res/insights/metric-alert/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/insights/private-link-scope/README.md b/avm/res/insights/private-link-scope/README.md index 33afa6a34b..cbed0f5ba9 100644 --- a/avm/res/insights/private-link-scope/README.md +++ b/avm/res/insights/private-link-scope/README.md @@ -19,8 +19,8 @@ This module deploys an Azure Monitor Private Link Scope. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `microsoft.insights/privateLinkScopes` | [2021-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2021-07-01-preview/privateLinkScopes) | | `Microsoft.Insights/privateLinkScopes/scopedResources` | [2021-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-07-01-preview/privateLinkScopes/scopedResources) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -208,9 +208,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: } ] name: 'pe-' - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { principalId: '' @@ -228,11 +232,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: ] roleAssignments: [ { + name: 'af62023f-9f34-4bc0-8f05-2374886daf28' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -392,9 +398,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: } ], "name": "pe-", - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { "principalId": "", @@ -414,11 +424,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: "roleAssignments": { "value": [ { + "name": "af62023f-9f34-4bc0-8f05-2374886daf28", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -563,9 +575,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: } ] name: 'pe-' - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -704,9 +720,13 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: } ], "name": "pe-", - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -939,8 +959,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1124,19 +1143,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1174,6 +1238,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1224,6 +1289,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1277,6 +1349,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1327,6 +1400,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1386,6 +1466,7 @@ Resource tags. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the private link scope. | +| `privateEndpoints` | array | The private endpoints of the private link scope. | | `resourceGroupName` | string | The resource group the private link scope was deployed into. | | `resourceId` | string | The resource ID of the private link scope. | @@ -1395,7 +1476,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/insights/private-link-scope/main.bicep b/avm/res/insights/private-link-scope/main.bicep index cf8084d4af..50534b0383 100644 --- a/avm/res/insights/private-link-scope/main.bicep +++ b/avm/res/insights/private-link-scope/main.bicep @@ -83,6 +83,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-privatelinkscope.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -143,7 +154,7 @@ resource privateLinkScope_lock 'Microsoft.Authorization/locks@2020-05-01' = if ( scope: privateLinkScope } -module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-privateLinkScope-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -184,8 +195,7 @@ module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endp 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -197,14 +207,10 @@ module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endp ] resource privateLinkScope_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(privateLinkScope.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(privateLinkScope.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -228,6 +234,17 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = privateLinkScope.location +@description('The private endpoints of the private link scope.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: privateLinkScope_privateEndpoints[i].outputs.name + resourceId: privateLinkScope_privateEndpoints[i].outputs.resourceId + groupId: privateLinkScope_privateEndpoints[i].outputs.groupId + customDnsConfig: privateLinkScope_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: privateLinkScope_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -241,6 +258,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -279,11 +299,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/insights/private-link-scope/main.json b/avm/res/insights/private-link-scope/main.json index c813a5d470..c1efbd235d 100644 --- a/avm/res/insights/private-link-scope/main.json +++ b/avm/res/insights/private-link-scope/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6717691244294580216" + "version": "0.29.47.4906", + "templateHash": "9210191494689145931" }, "name": "Azure Monitor Private Link Scopes", "description": "This module deploys an Azure Monitor Private Link Scope.", @@ -43,6 +43,13 @@ "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": { @@ -143,21 +150,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -450,6 +480,13 @@ } }, "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')]", @@ -515,20 +552,20 @@ "privateLinkScope_roleAssignments": { "copy": { "name": "privateLinkScope_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('microsoft.insights/privateLinkScopes/{0}', parameters('name'))]", - "name": "[guid(resourceId('microsoft.insights/privateLinkScopes', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('microsoft.insights/privateLinkScopes', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "privateLinkScope" @@ -564,8 +601,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2552443205161767584" + "version": "0.29.47.4906", + "templateHash": "15704480901641345186" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource.", @@ -664,11 +701,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -696,19 +730,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -812,13 +874,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -852,8 +914,11 @@ "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." + "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": { @@ -893,8 +958,11 @@ "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." + "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": { @@ -942,6 +1010,29 @@ } }, "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": { @@ -977,18 +1068,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1044,6 +1128,13 @@ } }, "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')]", @@ -1061,8 +1152,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1080,7 +1171,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1121,27 +1212,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1152,28 +1243,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1181,12 +1296,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1200,27 +1318,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1278,14 +1405,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1323,6 +1464,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('privateLinkScope', '2021-07-01-preview', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the private link scope." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('privateLinkScope_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/insights/private-link-scope/scoped-resource/main.json b/avm/res/insights/private-link-scope/scoped-resource/main.json index 7d9670d70f..57e0b76f73 100644 --- a/avm/res/insights/private-link-scope/scoped-resource/main.json +++ b/avm/res/insights/private-link-scope/scoped-resource/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2552443205161767584" + "version": "0.29.47.4906", + "templateHash": "15704480901641345186" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource.", diff --git a/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep b/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep index 5b2e9ec8d5..25c575033e 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/max/main.test.bicep @@ -78,9 +78,13 @@ module testDeployment '../../../main.bicep' = [ name: 'pe-${namePrefix}' customNetworkInterfaceName: 'nic-pe-${namePrefix}' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -187,11 +191,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'af62023f-9f34-4bc0-8f05-2374886daf28' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep index c92cf66a1d..5a537cd3fe 100644 --- a/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/private-link-scope/tests/e2e/waf-aligned/main.test.bicep @@ -65,9 +65,13 @@ module testDeployment '../../../main.bicep' = [ name: 'pe-${namePrefix}' customNetworkInterfaceName: 'nic-pe-${namePrefix}' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/insights/private-link-scope/version.json b/avm/res/insights/private-link-scope/version.json index c177b1bb58..a8eda31021 100644 --- a/avm/res/insights/private-link-scope/version.json +++ b/avm/res/insights/private-link-scope/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/scheduled-query-rule/README.md b/avm/res/insights/scheduled-query-rule/README.md index 4d4f1f1904..1da0e18f3f 100644 --- a/avm/res/insights/scheduled-query-rule/README.md +++ b/avm/res/insights/scheduled-query-rule/README.md @@ -16,7 +16,7 @@ This module deploys a Scheduled Query Rule. | Resource Type | API Version | | :-- | :-- | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Insights/scheduledQueryRules` | [2021-02-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-02-01-preview/scheduledQueryRules) | +| `Microsoft.Insights/scheduledQueryRules` | [2023-03-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2023-03-15-preview/scheduledQueryRules) | ## Usage examples @@ -197,17 +197,20 @@ module scheduledQueryRule 'br/public:avm/res/insights/scheduled-query-rule:' autoMitigate: false evaluationFrequency: 'PT5M' location: '' queryTimeRange: 'PT5M' roleAssignments: [ { + name: 'fa8868c7-33d3-4cd5-86a5-cbf76261035b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -218,6 +221,10 @@ module scheduledQueryRule 'br/public:avm/res/insights/scheduled-query-rule:' } ] + ruleResolveConfiguration: { + autoResolved: true + timeToResolve: 'PT5M' + } suppressForMinutes: 'PT5M' tags: { Environment: 'Non-Prod' @@ -283,6 +290,9 @@ module scheduledQueryRule 'br/public:avm/res/insights/scheduled-query-rule:" + }, "autoMitigate": { "value": false }, @@ -298,11 +308,13 @@ module scheduledQueryRule 'br/public:avm/res/insights/scheduled-query-rule:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -314,6 +326,12 @@ module scheduledQueryRule 'br/public:avm/res/insights/scheduled-query-rule:' = { } roleAssignments: [ { + name: '86bf66a0-940f-438d-977e-624c00ccb2d8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -204,11 +206,13 @@ module webtest 'br/public:avm/res/insights/webtest:' = { "roleAssignments": { "value": [ { + "name": "86bf66a0-940f-438d-977e-624c00ccb2d8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -393,7 +397,6 @@ An XML configuration specification for a WebTest. - Required: No - Type: object -- Default: `{}` ### Parameter: `description` @@ -544,6 +547,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -594,6 +598,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/insights/webtest/main.bicep b/avm/res/insights/webtest/main.bicep index 6f633b1e2b..2bb6ca55cc 100644 --- a/avm/res/insights/webtest/main.bicep +++ b/avm/res/insights/webtest/main.bicep @@ -69,7 +69,7 @@ param retryEnabled bool = true param validationRules object = {} @sys.description('Optional. An XML configuration specification for a WebTest.') -param configuration object = {} +param configuration object? @sys.description('Optional. The lock settings of the service.') param lock lockType @@ -98,6 +98,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.insights-webtest.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -149,14 +160,10 @@ resource webtest_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource webtest_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(webtest.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(webtest.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -193,6 +200,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/insights/webtest/main.json b/avm/res/insights/webtest/main.json index af59a0911c..d957690c50 100644 --- a/avm/res/insights/webtest/main.json +++ b/avm/res/insights/webtest/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16811584526050573823" + "version": "0.27.1.19265", + "templateHash": "7223657716552669834" }, "name": "Web Tests", "description": "This module deploys a Web Test.", @@ -43,6 +43,13 @@ "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": { @@ -230,7 +237,7 @@ }, "configuration": { "type": "object", - "defaultValue": {}, + "nullable": true, "metadata": { "description": "Optional. An XML configuration specification for a WebTest." } @@ -256,6 +263,13 @@ } }, "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)))))]" + } + ], "hiddenLinkTag": { "[format('hidden-link:{0}', parameters('appInsightResourceId'))]": "Resource" }, @@ -326,20 +340,20 @@ "webtest_roleAssignments": { "copy": { "name": "webtest_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Insights/webtests/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Insights/webtests', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/webtests', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "webtest" diff --git a/avm/res/insights/webtest/tests/e2e/max/main.test.bicep b/avm/res/insights/webtest/tests/e2e/max/main.test.bicep index 351a8baa6e..aaafa6341f 100644 --- a/avm/res/insights/webtest/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/webtest/tests/e2e/max/main.test.bicep @@ -73,11 +73,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '86bf66a0-940f-438d-977e-624c00ccb2d8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/insights/webtest/version.json b/avm/res/insights/webtest/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/insights/webtest/version.json +++ b/avm/res/insights/webtest/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/key-vault/vault/README.md b/avm/res/key-vault/vault/README.md index f8e4cfc1f1..f6d19e1046 100644 --- a/avm/res/key-vault/vault/README.md +++ b/avm/res/key-vault/vault/README.md @@ -22,8 +22,8 @@ This module deploys a Key Vault. | `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | | `Microsoft.KeyVault/vaults/keys` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/keys) | | `Microsoft.KeyVault/vaults/secrets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/secrets) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -369,9 +369,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } roleAssignments: [ { principalId: '' @@ -392,19 +396,25 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: 'b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -623,9 +633,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "roleAssignments": [ { "principalId": "", @@ -646,9 +660,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -656,11 +674,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { "roleAssignments": { "value": [ { + "name": "b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -910,9 +930,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'vault' subnetResourceId: '' } @@ -1024,9 +1048,13 @@ module vault 'br/public:avm/res/key-vault/vault:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "vault", "subnetResourceId": "" } @@ -1670,6 +1698,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-keysroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-keysroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-keysroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-keysroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-keysroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `keys.roleAssignments.principalId` @@ -1720,6 +1749,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `keys.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `keys.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1925,8 +1961,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -2110,19 +2145,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -2160,6 +2240,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -2210,6 +2291,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2279,6 +2367,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2329,6 +2418,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2448,6 +2544,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-secretsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-secretsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-secretsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-secretsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-secretsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `secrets.roleAssignments.principalId` @@ -2498,6 +2595,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `secrets.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `secrets.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2559,6 +2663,7 @@ Resource tags. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the key vault. | +| `privateEndpoints` | array | The private endpoints of the key vault. | | `resourceGroupName` | string | The name of the resource group the key vault was created in. | | `resourceId` | string | The resource ID of the key vault. | | `uri` | string | The URI of the key vault. | @@ -2569,7 +2674,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/key-vault/vault/access-policy/main.json b/avm/res/key-vault/vault/access-policy/main.json index ef2eebf086..ba6a0ab33f 100644 --- a/avm/res/key-vault/vault/access-policy/main.json +++ b/avm/res/key-vault/vault/access-policy/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9363032158799379271" + "version": "0.29.47.4906", + "templateHash": "7494731697751039419" }, "name": "Key Vault Access Policies", "description": "This module deploys a Key Vault Access Policy.", diff --git a/avm/res/key-vault/vault/key/README.md b/avm/res/key-vault/vault/key/README.md index 16a2230c28..158fada9d8 100644 --- a/avm/res/key-vault/vault/key/README.md +++ b/avm/res/key-vault/vault/key/README.md @@ -172,6 +172,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -222,6 +223,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/key-vault/vault/key/main.bicep b/avm/res/key-vault/vault/key/main.bicep index 300c781271..82eef70abd 100644 --- a/avm/res/key-vault/vault/key/main.bicep +++ b/avm/res/key-vault/vault/key/main.bicep @@ -100,6 +100,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } @@ -124,14 +135,10 @@ resource key 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { } resource key_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(key.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(key.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -157,6 +164,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/key-vault/vault/key/main.json b/avm/res/key-vault/vault/key/main.json index cc9eab1b6d..4292df4c7a 100644 --- a/avm/res/key-vault/vault/key/main.json +++ b/avm/res/key-vault/vault/key/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6463446681908714455" + "version": "0.29.47.4906", + "templateHash": "10436564794447478489" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key.", @@ -18,6 +18,13 @@ "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": { @@ -192,6 +199,13 @@ } }, "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')]", @@ -238,20 +252,20 @@ "key_roleAssignments": { "copy": { "name": "key_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep index e271380b64..b804b867e7 100644 --- a/avm/res/key-vault/vault/main.bicep +++ b/avm/res/key-vault/vault/main.bicep @@ -139,6 +139,17 @@ var builtInRoleNames = { ) } +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 formattedAccessPolicies = [ for accessPolicy in (accessPolicies ?? []): { applicationId: accessPolicy.?applicationId ?? '' @@ -259,9 +270,9 @@ module keyVault_secrets 'secret/main.bicep' = [ name: secret.name value: secret.value keyVaultName: keyVault.name - attributesEnabled: secret.?attributesEnabled - attributesExp: secret.?attributesExp - attributesNbf: secret.?attributesNbf + attributesEnabled: secret.?attributes.?enabled + attributesExp: secret.?attributes.?exp + attributesNbf: secret.?attributes.?nbf contentType: secret.?contentType tags: secret.?tags ?? tags roleAssignments: secret.?roleAssignments @@ -275,9 +286,9 @@ module keyVault_keys 'key/main.bicep' = [ params: { name: key.name keyVaultName: keyVault.name - attributesEnabled: key.?attributesEnabled - attributesExp: key.?attributesExp - attributesNbf: key.?attributesNbf + attributesEnabled: key.?attributes.?enabled + attributesExp: key.?attributes.?exp + attributesNbf: key.?attributes.?nbf curveName: (key.?kty != 'RSA' && key.?kty != 'RSA-HSM') ? (key.?curveName ?? 'P-256') : null keyOps: key.?keyOps keySize: (key.?kty == 'RSA' || key.?kty == 'RSA-HSM') ? (key.?keySize ?? 4096) : null @@ -290,7 +301,7 @@ module keyVault_keys 'key/main.bicep' = [ } ] -module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-keyVault-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -331,8 +342,7 @@ module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -344,14 +354,10 @@ module keyVault_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4 ] resource keyVault_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(keyVault.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(keyVault.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -381,6 +387,17 @@ output uri string = keyVault.properties.vaultUri @description('The location the resource was deployed into.') output location string = keyVault.location +@description('The private endpoints of the key vault.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: keyVault_privateEndpoints[i].outputs.name + resourceId: keyVault_privateEndpoints[i].outputs.resourceId + groupId: keyVault_privateEndpoints[i].outputs.groupId + customDnsConfig: keyVault_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: keyVault_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // ================ // // Definitions // // ================ // @@ -430,6 +447,9 @@ type diagnosticSettingType = { }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -468,11 +488,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? + + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json index 18b9592e0f..15f25dc8bb 100644 --- a/avm/res/key-vault/vault/main.json +++ b/avm/res/key-vault/vault/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16421400914086569012" + "version": "0.29.47.4906", + "templateHash": "535497364453680203" }, "name": "Key Vaults", "description": "This module deploys a Key Vault.", @@ -138,6 +138,13 @@ "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": { @@ -238,21 +245,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -1012,6 +1042,11 @@ }, "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()))]", @@ -1145,20 +1180,20 @@ "keyVault_roleAssignments": { "copy": { "name": "keyVault_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.KeyVault/vaults', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1189,8 +1224,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9363032158799379271" + "version": "0.29.47.4906", + "templateHash": "7494731697751039419" }, "name": "Key Vault Access Policies", "description": "This module deploys a Key Vault Access Policy.", @@ -1433,13 +1468,13 @@ "value": "[parameters('name')]" }, "attributesEnabled": { - "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesEnabled')]" + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'enabled')]" }, "attributesExp": { - "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesExp')]" + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'exp')]" }, "attributesNbf": { - "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributesNbf')]" + "value": "[tryGet(tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'attributes'), 'nbf')]" }, "contentType": { "value": "[tryGet(coalesce(parameters('secrets'), createArray())[copyIndex()], 'contentType')]" @@ -1458,8 +1493,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8081761451262814691" + "version": "0.29.47.4906", + "templateHash": "4990258423482296566" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret.", @@ -1471,6 +1506,13 @@ "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": { @@ -1595,6 +1637,13 @@ } }, "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')]", @@ -1636,20 +1685,20 @@ "secret_roleAssignments": { "copy": { "name": "secret_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1706,13 +1755,13 @@ "value": "[parameters('name')]" }, "attributesEnabled": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesEnabled')]" + "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'enabled')]" }, "attributesExp": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributesExp')]" + "value": "[tryGet(tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], 'attributes'), 'exp')]" }, "attributesNbf": { - "value": "[tryGet(coalesce(parameters('keys'), createArray())[copyIndex()], '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": { @@ -1742,8 +1791,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6463446681908714455" + "version": "0.29.47.4906", + "templateHash": "10436564794447478489" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key.", @@ -1755,6 +1804,13 @@ "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": { @@ -1929,6 +1985,13 @@ } }, "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')]", @@ -1975,20 +2038,20 @@ "key_roleAssignments": { "copy": { "name": "key_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.KeyVault/vaults/keys', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2056,11 +2119,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2088,19 +2148,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2204,13 +2292,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2244,8 +2332,11 @@ "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." + "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": { @@ -2285,8 +2376,11 @@ "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." + "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": { @@ -2334,6 +2428,29 @@ } }, "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": { @@ -2369,18 +2486,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2436,6 +2546,13 @@ } }, "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')]", @@ -2453,8 +2570,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2472,7 +2589,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2513,27 +2630,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -2544,28 +2661,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -2573,12 +2714,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -2592,27 +2736,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2670,14 +2823,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2722,6 +2889,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('keyVault', '2022-07-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the key vault." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('keyVault_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/key-vault/vault/secret/README.md b/avm/res/key-vault/vault/secret/README.md index ec52ca08fa..3be339af0f 100644 --- a/avm/res/key-vault/vault/secret/README.md +++ b/avm/res/key-vault/vault/secret/README.md @@ -115,6 +115,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -165,6 +166,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/key-vault/vault/secret/main.bicep b/avm/res/key-vault/vault/secret/main.bicep index 0c4db8dfbe..2145488780 100644 --- a/avm/res/key-vault/vault/secret/main.bicep +++ b/avm/res/key-vault/vault/secret/main.bicep @@ -65,6 +65,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } @@ -85,14 +96,10 @@ resource secret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { } resource secret_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(secret.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(secret.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -118,6 +125,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/key-vault/vault/secret/main.json b/avm/res/key-vault/vault/secret/main.json index fa83a7e6e2..8ae75bb1d3 100644 --- a/avm/res/key-vault/vault/secret/main.json +++ b/avm/res/key-vault/vault/secret/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8081761451262814691" + "version": "0.29.47.4906", + "templateHash": "4990258423482296566" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret.", @@ -18,6 +18,13 @@ "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": { @@ -142,6 +149,13 @@ } }, "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')]", @@ -183,20 +197,20 @@ "secret_roleAssignments": { "copy": { "name": "secret_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/key-vault/vault/tests/e2e/max/dependencies.bicep b/avm/res/key-vault/vault/tests/e2e/max/dependencies.bicep index f433490224..6c3754d07f 100644 --- a/avm/res/key-vault/vault/tests/e2e/max/dependencies.bicep +++ b/avm/res/key-vault/vault/tests/e2e/max/dependencies.bicep @@ -62,4 +62,4 @@ output subnetResourceId string = virtualNetwork.properties.subnets[0].id output managedIdentityPrincipalId string = managedIdentity.properties.principalId @description('The resource ID of the created Private DNS Zone.') -output privateDNSResourceId string = privateDNSZone.id +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep index 2cceaafc54..08693fdd86 100644 --- a/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep +++ b/avm/res/key-vault/vault/tests/e2e/max/main.test.bicep @@ -192,9 +192,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -236,19 +240,25 @@ module testDeployment '../../../main.bicep' = [ ] } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] roleAssignments: [ { + name: 'b50cc72e-a2f2-4c4c-a3ad-86a43feb6ab8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep index f433490224..6c3754d07f 100644 --- a/avm/res/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/key-vault/vault/tests/e2e/waf-aligned/dependencies.bicep @@ -62,4 +62,4 @@ output subnetResourceId string = virtualNetwork.properties.subnets[0].id output managedIdentityPrincipalId string = managedIdentity.properties.principalId @description('The resource ID of the created Private DNS Zone.') -output privateDNSResourceId string = privateDNSZone.id +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep b/avm/res/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep index f75788795d..88763ac590 100644 --- a/avm/res/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/key-vault/vault/tests/e2e/waf-aligned/main.test.bicep @@ -122,9 +122,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'vault' subnetResourceId: nestedDependencies.outputs.subnetResourceId } diff --git a/avm/res/key-vault/vault/version.json b/avm/res/key-vault/vault/version.json index 9ed3662aba..0f81d22abc 100644 --- a/avm/res/key-vault/vault/version.json +++ b/avm/res/key-vault/vault/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.8", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/kusto/cluster/README.md b/avm/res/kusto/cluster/README.md index 0df19d1c41..6f79b14c35 100644 --- a/avm/res/kusto/cluster/README.md +++ b/avm/res/kusto/cluster/README.md @@ -20,8 +20,8 @@ This module deploys a Kusto Cluster. | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Kusto/clusters` | [2023-08-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Kusto/2023-08-15/clusters) | | `Microsoft.Kusto/clusters/principalAssignments` | [2023-08-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Kusto/2023-08-15/clusters/principalAssignments) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -33,7 +33,8 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) -- [WAF-aligned](#example-3-waf-aligned) +- [Private endpoint-enabled deployment](#example-3-private-endpoint-enabled-deployment) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -148,11 +149,13 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { publicIPType: 'DualStack' roleAssignments: [ { + name: 'c2a4b728-c3d0-47f5-afbb-ea45c45859de' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -274,11 +277,13 @@ module cluster 'br/public:avm/res/kusto/cluster:' = { "roleAssignments": { "value": [ { + "name": "c2a4b728-c3d0-47f5-afbb-ea45c45859de", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -297,7 +302,129 @@ module cluster 'br/public:avm/res/kusto/cluster:' = {

-### Example 3: _WAF-aligned_ +### Example 3: _Private endpoint-enabled deployment_ + +This instance deploys the module with private endpoints. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/kusto/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + name: 'akcpe0001' + sku: 'Standard_E2ads_v5' + // Non-required parameters + enablePublicNetworkAccess: false + location: '' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } + service: 'cluster' + subnetResourceId: '' + } + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } + service: 'cluster' + subnetResourceId: '' + } + ] + publicIPType: 'IPv4' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "akcpe0001" + }, + "sku": { + "value": "Standard_E2ads_v5" + }, + // Non-required parameters + "enablePublicNetworkAccess": { + "value": false + }, + "location": { + "value": "" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, + "service": "cluster", + "subnetResourceId": "" + }, + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, + "service": "cluster", + "subnetResourceId": "" + } + ] + }, + "publicIPType": { + "value": "IPv4" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -970,7 +1097,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | Parameter | Type | Description | | :-- | :-- | :-- | -| [`service`](#parameter-privateendpointsservice) | string | The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". | | [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | **Optional parameters** @@ -982,19 +1109,20 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | | [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | -| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | Manual PrivateLink Service Connections. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | | [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | +| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | ### Parameter: `privateEndpoints.service` -The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob". +The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". - Required: Yes - Type: string @@ -1024,19 +1152,19 @@ Custom DNS configurations. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | -| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | ### Parameter: `privateEndpoints.customDnsConfigs.fqdn` -Fqdn that resolves to private endpoint ip address. +Fqdn that resolves to private endpoint IP address. - Required: No - Type: string ### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` -A list of private ip addresses of the private endpoint. +A list of private IP addresses of the private endpoint. - Required: Yes - Type: array @@ -1089,7 +1217,7 @@ Properties of private endpoint IP configurations. | :-- | :-- | :-- | | [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | | [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | -| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | ### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` @@ -1107,14 +1235,14 @@ The member name of a group obtained from the remote resource that this private e ### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` -A private ip address obtained from the private endpoint's subnet. +A private IP address obtained from the private endpoint's subnet. - Required: Yes - Type: string ### Parameter: `privateEndpoints.isManualConnection` -Manual PrivateLink Service Connections. +If Manual Private Link Connection is required. - Required: No - Type: bool @@ -1176,19 +1304,78 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. +The name of the Private DNS Zone Group. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private link connection to create. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.roleAssignments` @@ -1212,6 +1399,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1262,6 +1450,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1324,6 +1519,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1374,6 +1570,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1491,9 +1694,10 @@ The resource ID of the subnet to which to deploy the Kusto Cluster. | Output | Type | Description | | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | -| `name` | string | The name of the resource. | -| `resourceGroupName` | string | The resource group the resource was deployed into. | -| `resourceId` | string | The resource id of the resource. | +| `name` | string | The name of the kusto cluster. | +| `privateEndpoints` | array | The private endpoints of the kusto cluster. | +| `resourceGroupName` | string | The resource group the kusto cluster was deployed into. | +| `resourceId` | string | The resource id of the kusto cluster. | ## Cross-referenced modules @@ -1501,7 +1705,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.0` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/kusto/cluster/main.bicep b/avm/res/kusto/cluster/main.bicep index 08d39fa696..2a9802eb5e 100644 --- a/avm/res/kusto/cluster/main.bicep +++ b/avm/res/kusto/cluster/main.bicep @@ -140,6 +140,17 @@ var builtInRoleNames = { Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -262,14 +273,10 @@ resource kustoCluster_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!emp } resource kustoCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(kustoCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(kustoCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -294,12 +301,14 @@ module kustoCluster_principalAssignments 'principal-assignment/main.bicep' = [ } ] -module kustoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.0' = [ +@batchSize(1) +module kustoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-KustoCluster-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-kustoCluster-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(kustoCluster.id, '/'))}-${privateEndpoint.service}-${index}' - privateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections != true + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(kustoCluster.id, '/'))}-${privateEndpoint.service}-${index}' @@ -312,7 +321,7 @@ module kustoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint } ] : null - manualPrivateLinkServiceConnections: privateEndpoint.?manualPrivateLinkServiceConnections == true + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true ? [ { name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(kustoCluster.id, '/'))}-${privateEndpoint.service}-${index}' @@ -334,8 +343,7 @@ module kustoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -350,18 +358,29 @@ module kustoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint // Outputs // // ============ // -@description('The resource group the resource was deployed into.') +@description('The resource group the kusto cluster was deployed into.') output resourceGroupName string = resourceGroup().name -@description('The resource id of the resource.') +@description('The resource id of the kusto cluster.') output resourceId string = kustoCluster.id -@description('The name of the resource.') +@description('The name of the kusto cluster.') output name string = kustoCluster.name @description('The location the resource was deployed into.') output location string = kustoCluster.location +@description('The private endpoints of the kusto cluster.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: kustoCluster_privateEndpoints[i].outputs.name + resourceId: kustoCluster_privateEndpoints[i].outputs.resourceId + groupId: kustoCluster_privateEndpoints[i].outputs.groupId + customDnsConfig: kustoCluster_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: kustoCluster_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -471,19 +490,31 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? - @description('Required. The service (sub-) type to deploy the private endpoint for. For example "vault" or "blob".') + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? - @description('Optional. Manual PrivateLink Service Connections.') + @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') @@ -492,10 +523,10 @@ type privateEndpointType = { @description('Optional. Custom DNS configurations.') customDnsConfigs: { - @description('Required. Fqdn that resolves to private endpoint ip address.') + @description('Required. Fqdn that resolves to private endpoint IP address.') fqdn: string? - @description('Required. A list of private ip addresses of the private endpoint.') + @description('Required. A list of private IP addresses of the private endpoint.') ipAddresses: string[] }[]? @@ -512,7 +543,7 @@ type privateEndpointType = { @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') memberName: string - @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') privateIPAddress: string } }[]? @@ -534,9 +565,15 @@ type privateEndpointType = { @description('Optional. Enable/Disable usage telemetry for module.') enableTelemetry: bool? + + @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') + resourceGroupName: string? }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/kusto/cluster/main.json b/avm/res/kusto/cluster/main.json index c8c5bc44c5..8135431606 100644 --- a/avm/res/kusto/cluster/main.json +++ b/avm/res/kusto/cluster/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9628291387730167491" + "version": "0.29.47.4906", + "templateHash": "4366309935211132094" }, "name": "Kusto Cluster", "description": "This module deploys a Kusto Cluster.", @@ -283,40 +283,70 @@ "description": "Optional. The location to deploy the private endpoint to." } }, - "service": { + "privateLinkServiceConnectionName": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + "description": "Optional. The name of the private link connection to create." } }, - "subnetResourceId": { + "service": { "type": "string", "metadata": { - "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." } }, - "privateDnsZoneGroupName": { + "subnetResourceId": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { "type": "bool", "nullable": true, "metadata": { - "description": "Optional. Manual PrivateLink Service Connections." + "description": "Optional. If Manual Private Link Connection is required." } }, "manualConnectionRequestMessage": { @@ -336,7 +366,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Required. Fqdn that resolves to private endpoint ip address." + "description": "Required. Fqdn that resolves to private endpoint IP address." } }, "ipAddresses": { @@ -345,7 +375,7 @@ "type": "string" }, "metadata": { - "description": "Required. A list of private ip addresses of the private endpoint." + "description": "Required. A list of private IP addresses of the private endpoint." } } } @@ -384,7 +414,7 @@ "privateIPAddress": { "type": "string", "metadata": { - "description": "Required. A private ip address obtained from the private endpoint's subnet." + "description": "Required. A private IP address obtained from the private endpoint's subnet." } } }, @@ -441,6 +471,13 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } } } }, @@ -451,6 +488,13 @@ "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": { @@ -817,6 +861,13 @@ } }, "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(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None'), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -966,20 +1017,20 @@ "kustoCluster_roleAssignments": { "copy": { "name": "kustoCluster_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Kusto/clusters/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Kusto/clusters', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Kusto/clusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "kustoCluster" @@ -1019,8 +1070,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11054528723768374288" + "version": "0.29.47.4906", + "templateHash": "15388375145433684348" }, "name": "Kusto Cluster Principal Assignments", "description": "This module deploys a Kusto Cluster Principal Assignment.", @@ -1115,11 +1166,14 @@ "kustoCluster_privateEndpoints": { "copy": { "name": "kustoCluster_privateEndpoints", - "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "mode": "serial", + "batchSize": 1 }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-KustoCluster-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-kustoCluster-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1129,8 +1183,8 @@ "name": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Kusto/clusters', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" }, - "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Kusto/clusters', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Kusto/clusters', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", - "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Kusto/clusters', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Kusto/clusters', 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()))]", + "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.Kusto/clusters', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Kusto/clusters', 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.Kusto/clusters', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Kusto/clusters', 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]" }, @@ -1143,11 +1197,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1175,19 +1226,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.24.24.22086", - "templateHash": "2592884001616184297" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1291,13 +1370,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1331,8 +1410,11 @@ "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." + "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": { @@ -1372,8 +1454,11 @@ "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." + "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": { @@ -1421,6 +1506,29 @@ } }, "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": { @@ -1456,18 +1564,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1523,6 +1624,13 @@ } }, "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')]", @@ -1540,8 +1648,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1559,7 +1667,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1600,27 +1708,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1631,28 +1739,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.24.24.22086", - "templateHash": "9321937464667207030" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1660,12 +1792,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1679,27 +1814,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1757,14 +1901,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1778,21 +1936,21 @@ "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the resource was deployed into." + "description": "The resource group the kusto cluster was deployed into." }, "value": "[resourceGroup().name]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource id of the resource." + "description": "The resource id of the kusto cluster." }, "value": "[resourceId('Microsoft.Kusto/clusters', parameters('name'))]" }, "name": { "type": "string", "metadata": { - "description": "The name of the resource." + "description": "The name of the kusto cluster." }, "value": "[parameters('name')]" }, @@ -1802,6 +1960,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('kustoCluster', '2023-08-15', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the kusto cluster." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('kustoCluster_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('kustoCluster_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('kustoCluster_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('kustoCluster_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('kustoCluster_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/kusto/cluster/principal-assignment/main.json b/avm/res/kusto/cluster/principal-assignment/main.json index 93fa1e0794..e58f3f6a1c 100644 --- a/avm/res/kusto/cluster/principal-assignment/main.json +++ b/avm/res/kusto/cluster/principal-assignment/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11054528723768374288" + "version": "0.29.47.4906", + "templateHash": "15388375145433684348" }, "name": "Kusto Cluster Principal Assignments", "description": "This module deploys a Kusto Cluster Principal Assignment.", diff --git a/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep index 411c2bd5b4..6a27624eaf 100644 --- a/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep +++ b/avm/res/kusto/cluster/tests/e2e/max/main.test.bicep @@ -97,11 +97,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'c2a4b728-c3d0-47f5-afbb-ea45c45859de' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/kusto/cluster/tests/e2e/pe/dependencies.bicep b/avm/res/kusto/cluster/tests/e2e/pe/dependencies.bicep new file mode 100644 index 0000000000..1b34a4ac45 --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/pe/dependencies.bicep @@ -0,0 +1,68 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + { + name: 'peTestSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + } + ] + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.digitaltwins.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Virtual Network Subnet.') +output pepTestSubnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep b/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep new file mode 100644 index 0000000000..bdb035d2c8 --- /dev/null +++ b/avm/res/kusto/cluster/tests/e2e/pe/main.test.bicep @@ -0,0 +1,92 @@ +targetScope = 'subscription' + +metadata name = 'Private endpoint-enabled deployment' +metadata description = 'This instance deploys the module with private endpoints.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-kusto.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'akcpe' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}0001' + location: resourceLocation + sku: 'Standard_E2ads_v5' + enablePublicNetworkAccess: false + publicIPType: 'IPv4' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.defaultSubnetResourceId + service: 'cluster' + } + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.pepTestSubnetResourceId + service: 'cluster' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/kusto/cluster/version.json b/avm/res/kusto/cluster/version.json index 1c884ecaa9..76049e1c4a 100644 --- a/avm/res/kusto/cluster/version.json +++ b/avm/res/kusto/cluster/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/load-test-service/load-test/README.md b/avm/res/load-test-service/load-test/README.md index 54ee6da52b..e5cb6676e2 100644 --- a/avm/res/load-test-service/load-test/README.md +++ b/avm/res/load-test-service/load-test/README.md @@ -106,9 +106,21 @@ module loadTest 'br/public:avm/res/load-test-service/load-test:' = { } roleAssignments: [ { + name: 'd37a15bc-8634-4f4f-a736-700c1b955cd7' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] tags: { @@ -156,9 +168,21 @@ module loadTest 'br/public:avm/res/load-test-service/load-test:' = { "roleAssignments": { "value": [ { + "name": "d37a15bc-8634-4f4f-a736-700c1b955cd7", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] }, @@ -503,6 +527,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -553,6 +578,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/load-test-service/load-test/main.bicep b/avm/res/load-test-service/load-test/main.bicep index d7f04bf9d8..3412a0df4c 100644 --- a/avm/res/load-test-service/load-test/main.bicep +++ b/avm/res/load-test-service/load-test/main.bicep @@ -66,6 +66,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ================ // // Resources // // ================ // @@ -142,14 +153,10 @@ resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { } resource loadTest_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(loadTest.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(loadTest.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -193,6 +200,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/load-test-service/load-test/main.json b/avm/res/load-test-service/load-test/main.json index a28f27e0fe..5f5d8ba0ab 100644 --- a/avm/res/load-test-service/load-test/main.json +++ b/avm/res/load-test-service/load-test/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7892040469195858850" + "version": "0.29.47.4906", + "templateHash": "10626999176786723279" }, "name": "Load Testing Service", "description": "This module deploys a Load test.", @@ -43,6 +43,13 @@ "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": { @@ -224,6 +231,13 @@ } }, "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": { @@ -318,20 +332,20 @@ "loadTest_roleAssignments": { "copy": { "name": "loadTest_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.LoadTestService/loadTests/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.LoadTestService/loadTests', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.LoadTestService/loadTests', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "loadTest" diff --git a/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep b/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep index e2a7ec1328..b79f18bbe3 100644 --- a/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep +++ b/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep @@ -55,7 +55,22 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: 'd37a15bc-8634-4f4f-a736-700c1b955cd7' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } diff --git a/avm/res/load-test-service/load-test/version.json b/avm/res/load-test-service/load-test/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/load-test-service/load-test/version.json +++ b/avm/res/load-test-service/load-test/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/logic/workflow/README.md b/avm/res/logic/workflow/README.md index fe42386b0e..97566dc6f1 100644 --- a/avm/res/logic/workflow/README.md +++ b/avm/res/logic/workflow/README.md @@ -123,11 +123,13 @@ module workflow 'br/public:avm/res/logic/workflow:' = { } roleAssignments: [ { + name: '1f98c16b-ea00-4686-8b81-05353b594ea3' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -230,11 +232,13 @@ module workflow 'br/public:avm/res/logic/workflow:' = { "roleAssignments": { "value": [ { + "name": "1f98c16b-ea00-4686-8b81-05353b594ea3", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -819,6 +823,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -869,6 +874,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/logic/workflow/main.bicep b/avm/res/logic/workflow/main.bicep index 2883a2f839..4e95f611cd 100644 --- a/avm/res/logic/workflow/main.bicep +++ b/avm/res/logic/workflow/main.bicep @@ -116,6 +116,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.logic-workflow.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -208,14 +219,10 @@ resource logicApp_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021 ] resource logicApp_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(logicApp.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(logicApp.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -312,6 +319,9 @@ type managedIdentitiesType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/logic/workflow/main.json b/avm/res/logic/workflow/main.json index c15891e14a..e434c0dc74 100644 --- a/avm/res/logic/workflow/main.json +++ b/avm/res/logic/workflow/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2565673093930379950" + "version": "0.29.47.4906", + "templateHash": "1894000925881202684" }, "name": "Logic Apps (Workflows)", "description": "This module deploys a Logic App (Workflow).", @@ -199,6 +199,13 @@ "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": { @@ -428,6 +435,13 @@ } }, "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()), 'SystemAssigned', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -552,20 +566,20 @@ "logicApp_roleAssignments": { "copy": { "name": "logicApp_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Logic/workflows/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Logic/workflows', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Logic/workflows', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "logicApp" diff --git a/avm/res/logic/workflow/tests/e2e/max/main.test.bicep b/avm/res/logic/workflow/tests/e2e/max/main.test.bicep index 9e912f3433..c15023250b 100644 --- a/avm/res/logic/workflow/tests/e2e/max/main.test.bicep +++ b/avm/res/logic/workflow/tests/e2e/max/main.test.bicep @@ -86,11 +86,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '1f98c16b-ea00-4686-8b81-05353b594ea3' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/logic/workflow/version.json b/avm/res/logic/workflow/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/logic/workflow/version.json +++ b/avm/res/logic/workflow/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/machine-learning-services/workspace/README.md b/avm/res/machine-learning-services/workspace/README.md index 94948ea6ab..b7f3752a25 100644 --- a/avm/res/machine-learning-services/workspace/README.md +++ b/avm/res/machine-learning-services/workspace/README.md @@ -19,10 +19,11 @@ This module deploys a Machine Learning Services Workspace. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.MachineLearningServices/workspaces` | [2024-04-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/workspaces) | +| `Microsoft.MachineLearningServices/workspaces` | [2024-04-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2024-04-01-preview/workspaces) | | `Microsoft.MachineLearningServices/workspaces/computes` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2022-10-01/workspaces/computes) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.MachineLearningServices/workspaces/connections` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2024-04-01/workspaces/connections) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -59,6 +60,26 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:' associatedKeyVaultResourceId: '' associatedStorageAccountResourceId: '' + connections: [ + { + category: 'AIServices' + connectionProperties: { + authType: 'ApiKey' + credentials: { + key: 'key' + } + } + metadata: { + ApiType: 'Azure' + ApiVersion: '2023-07-01-preview' + DeploymentApiVersion: '2023-10-01-preview' + Location: '' + ResourceId: '' + } + name: 'ai' + target: '' + } + ] kind: 'Hub' location: '' workspaceHubConfig: { @@ -98,6 +119,28 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:" }, + "connections": { + "value": [ + { + "category": "AIServices", + "connectionProperties": { + "authType": "ApiKey", + "credentials": { + "key": "key" + } + }, + "metadata": { + "ApiType": "Azure", + "ApiVersion": "2023-07-01-preview", + "DeploymentApiVersion": "2023-10-01-preview", + "Location": "", + "ResourceId": "" + }, + "name": "ai", + "target": "" + } + ] + }, "kind": { "value": "Hub" }, @@ -433,6 +476,19 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:' privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + name: 'group1' + privateDnsZoneGroupConfigs: [ + { + name: 'config1' + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -479,19 +541,27 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:' - ] + privateDnsZoneGroup: { + name: 'group2' + privateDnsZoneGroupConfigs: [ + { + name: 'config2' + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: 'f9b5b0d9-f27e-4c89-bacf-1bbc4a99dbce' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -577,6 +647,21 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:" - ], + "privateDnsZoneGroup": { + "name": "group1", + "privateDnsZoneGroupConfigs": [ + { + "name": "config1", + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -644,9 +735,15 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:" - ], + "privateDnsZoneGroup": { + "name": "group2", + "privateDnsZoneGroupConfigs": [ + { + "name": "config2", + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -654,11 +751,13 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -752,9 +851,13 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -848,9 +951,13 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -904,6 +1011,7 @@ module workspace 'br/public:avm/res/machine-learning-services/workspace:Any_other_property<`](#parameter-connectionsmetadata>any_other_property<) | string | The metadata key-value pairs. | + +### Parameter: `connections.metadata.>Any_other_property<` + +The metadata key-value pairs. + +- Required: Yes +- Type: string + +### Parameter: `connections.sharedUserList` + +The shared user list of the connection. + +- Required: No +- Type: array + +### Parameter: `connections.value` + +Value details of the workspace connection. + +- Required: No +- Type: string + ### Parameter: `customerManagedKey` The customer managed key definition. @@ -1455,8 +1773,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1640,19 +1957,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1690,6 +2052,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1740,6 +2103,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1808,6 +2178,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1858,6 +2229,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1983,7 +2361,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.0` | Remote reference | ## Notes diff --git a/avm/res/machine-learning-services/workspace/compute/main.json b/avm/res/machine-learning-services/workspace/compute/main.json index fb90ed3c1d..24fa048bd0 100644 --- a/avm/res/machine-learning-services/workspace/compute/main.json +++ b/avm/res/machine-learning-services/workspace/compute/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16086831886520789699" + "version": "0.29.47.4906", + "templateHash": "8580750401363518569" }, "name": "Machine Learning Services Workspaces Computes", "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).", diff --git a/avm/res/machine-learning-services/workspace/connection/README.md b/avm/res/machine-learning-services/workspace/connection/README.md new file mode 100644 index 0000000000..7321660f73 --- /dev/null +++ b/avm/res/machine-learning-services/workspace/connection/README.md @@ -0,0 +1,247 @@ +# Machine Learning Services Workspaces Connections `[Microsoft.MachineLearningServices/workspaces/connections]` + +This module creates a connection in a Machine Learning Services workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.MachineLearningServices/workspaces/connections` | [2024-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2024-04-01/workspaces/connections) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-category) | string | Category of the connection. | +| [`connectionProperties`](#parameter-connectionproperties) | secureObject | The properties of the connection, specific to the auth type. | +| [`machineLearningWorkspaceName`](#parameter-machinelearningworkspacename) | string | The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment. | +| [`name`](#parameter-name) | string | Name of the connection to create. | +| [`target`](#parameter-target) | string | The target of the connection. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`expiryTime`](#parameter-expirytime) | string | The expiry time of the connection. | +| [`isSharedToAll`](#parameter-issharedtoall) | bool | Indicates whether the connection is shared to all users in the workspace. | +| [`metadata`](#parameter-metadata) | object | User metadata for the connection. | +| [`sharedUserList`](#parameter-shareduserlist) | array | The shared user list of the connection. | +| [`value`](#parameter-value) | string | Value details of the workspace connection. | + +### Parameter: `category` + +Category of the connection. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'ADLSGen2' + 'AIServices' + 'AmazonMws' + 'AmazonRdsForOracle' + 'AmazonRdsForSqlServer' + 'AmazonRedshift' + 'AmazonS3Compatible' + 'ApiKey' + 'AzureBlob' + 'AzureDatabricksDeltaLake' + 'AzureDataExplorer' + 'AzureMariaDb' + 'AzureMySqlDb' + 'AzureOneLake' + 'AzureOpenAI' + 'AzurePostgresDb' + 'AzureSqlDb' + 'AzureSqlMi' + 'AzureSynapseAnalytics' + 'AzureTableStorage' + 'BingLLMSearch' + 'Cassandra' + 'CognitiveSearch' + 'CognitiveService' + 'Concur' + 'ContainerRegistry' + 'CosmosDb' + 'CosmosDbMongoDbApi' + 'Couchbase' + 'CustomKeys' + 'Db2' + 'Drill' + 'Dynamics' + 'DynamicsAx' + 'DynamicsCrm' + 'Eloqua' + 'FileServer' + 'FtpServer' + 'GenericContainerRegistry' + 'GenericHttp' + 'GenericRest' + 'Git' + 'GoogleAdWords' + 'GoogleBigQuery' + 'GoogleCloudStorage' + 'Greenplum' + 'Hbase' + 'Hdfs' + 'Hive' + 'Hubspot' + 'Impala' + 'Informix' + 'Jira' + 'Magento' + 'MariaDb' + 'Marketo' + 'MicrosoftAccess' + 'MongoDbAtlas' + 'MongoDbV2' + 'MySql' + 'Netezza' + 'ODataRest' + 'Odbc' + 'Office365' + 'OpenAI' + 'Oracle' + 'OracleCloudStorage' + 'OracleServiceCloud' + 'PayPal' + 'Phoenix' + 'PostgreSql' + 'Presto' + 'PythonFeed' + 'QuickBooks' + 'Redis' + 'Responsys' + 'S3' + 'Salesforce' + 'SalesforceMarketingCloud' + 'SalesforceServiceCloud' + 'SapBw' + 'SapCloudForCustomer' + 'SapEcc' + 'SapHana' + 'SapOpenHub' + 'SapTable' + 'Serp' + 'Serverless' + 'ServiceNow' + 'Sftp' + 'SharePointOnlineList' + 'Shopify' + 'Snowflake' + 'Spark' + 'SqlServer' + 'Square' + 'Sybase' + 'Teradata' + 'Vertica' + 'WebTable' + 'Xero' + 'Zoho' + ] + ``` + +### Parameter: `connectionProperties` + +The properties of the connection, specific to the auth type. + +- Required: Yes +- Type: secureObject + +### Parameter: `machineLearningWorkspaceName` + +The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Name of the connection to create. + +- Required: Yes +- Type: string + +### Parameter: `target` + +The target of the connection. + +- Required: Yes +- Type: string + +### Parameter: `expiryTime` + +The expiry time of the connection. + +- Required: No +- Type: string + +### Parameter: `isSharedToAll` + +Indicates whether the connection is shared to all users in the workspace. + +- Required: No +- Type: bool + +### Parameter: `metadata` + +User metadata for the connection. + +- Required: No +- Type: object +- Default: `{}` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`>Any_other_property<`](#parameter-metadata>any_other_property<) | string | The metadata key-value pairs. | + +### Parameter: `metadata.>Any_other_property<` + +The metadata key-value pairs. + +- Required: Yes +- Type: string + +### Parameter: `sharedUserList` + +The shared user list of the connection. + +- Required: No +- Type: array + +### Parameter: `value` + +Value details of the workspace connection. + +- Required: No +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the connection. | +| `resourceGroupName` | string | The name of the resource group the connection was created in. | +| `resourceId` | string | The resource ID of the connection. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/machine-learning-services/workspace/connection/main.bicep b/avm/res/machine-learning-services/workspace/connection/main.bicep new file mode 100644 index 0000000000..0aeab2a4f7 --- /dev/null +++ b/avm/res/machine-learning-services/workspace/connection/main.bicep @@ -0,0 +1,393 @@ +metadata name = 'Machine Learning Services Workspaces Connections' +metadata description = 'This module creates a connection in a Machine Learning Services workspace.' +metadata owner = 'Azure/module-maintainers' + +// ================ // +// Parameters // +// ================ // + +@description('Required. Name of the connection to create.') +param name string + +@description('Required. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment.') +param machineLearningWorkspaceName string + +@description('Required. Category of the connection.') +param category categoryType + +@description('Optional. The expiry time of the connection.') +param expiryTime string? + +@description('Optional. Indicates whether the connection is shared to all users in the workspace.') +param isSharedToAll bool? + +@description('Optional. User metadata for the connection.') +param metadata metadataType = {} + +@description('Optional. The shared user list of the connection.') +param sharedUserList string[]? + +@description('Required. The target of the connection.') +param target string + +@description('Optional. Value details of the workspace connection.') +param value string? + +@description('Required. The properties of the connection, specific to the auth type.') +param connectionProperties connectionPropertyType + +// ============================= // +// Existing resources references // +// ============================= // + +resource machineLearningWorkspace 'Microsoft.MachineLearningServices/workspaces@2022-10-01' existing = { + name: machineLearningWorkspaceName +} + +// ============== // +// Resources // +// ============== // + +resource connection 'Microsoft.MachineLearningServices/workspaces/connections@2024-04-01' = { + name: name + parent: machineLearningWorkspace + properties: union( + { + category: category + expiryTime: expiryTime + isSharedToAll: isSharedToAll + metadata: metadata + sharedUserList: sharedUserList + target: target + value: value + }, + connectionProperties + ) +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the connection.') +output resourceId string = connection.id + +@description('The name of the connection.') +output name string = connection.name + +@description('The name of the resource group the connection was created in.') +output resourceGroupName string = resourceGroup().name + +// ================ // +// Definitions // +// ================ // + +type metadataType = { + @description('Required. The metadata key-value pairs.') + *: string +} + +@export() +type categoryType = + | 'ADLSGen2' + | 'AIServices' + | 'AmazonMws' + | 'AmazonRdsForOracle' + | 'AmazonRdsForSqlServer' + | 'AmazonRedshift' + | 'AmazonS3Compatible' + | 'ApiKey' + | 'AzureBlob' + | 'AzureDataExplorer' + | 'AzureDatabricksDeltaLake' + | 'AzureMariaDb' + | 'AzureMySqlDb' + | 'AzureOneLake' + | 'AzureOpenAI' + | 'AzurePostgresDb' + | 'AzureSqlDb' + | 'AzureSqlMi' + | 'AzureSynapseAnalytics' + | 'AzureTableStorage' + | 'BingLLMSearch' + | 'Cassandra' + | 'CognitiveSearch' + | 'CognitiveService' + | 'Concur' + | 'ContainerRegistry' + | 'CosmosDb' + | 'CosmosDbMongoDbApi' + | 'Couchbase' + | 'CustomKeys' + | 'Db2' + | 'Drill' + | 'Dynamics' + | 'DynamicsAx' + | 'DynamicsCrm' + | 'Eloqua' + | 'FileServer' + | 'FtpServer' + | 'GenericContainerRegistry' + | 'GenericHttp' + | 'GenericRest' + | 'Git' + | 'GoogleAdWords' + | 'GoogleBigQuery' + | 'GoogleCloudStorage' + | 'Greenplum' + | 'Hbase' + | 'Hdfs' + | 'Hive' + | 'Hubspot' + | 'Impala' + | 'Informix' + | 'Jira' + | 'Magento' + | 'MariaDb' + | 'Marketo' + | 'MicrosoftAccess' + | 'MongoDbAtlas' + | 'MongoDbV2' + | 'MySql' + | 'Netezza' + | 'ODataRest' + | 'Odbc' + | 'Office365' + | 'OpenAI' + | 'Oracle' + | 'OracleCloudStorage' + | 'OracleServiceCloud' + | 'PayPal' + | 'Phoenix' + | 'PostgreSql' + | 'Presto' + | 'PythonFeed' + | 'QuickBooks' + | 'Redis' + | 'Responsys' + | 'S3' + | 'Salesforce' + | 'SalesforceMarketingCloud' + | 'SalesforceServiceCloud' + | 'SapBw' + | 'SapCloudForCustomer' + | 'SapEcc' + | 'SapHana' + | 'SapOpenHub' + | 'SapTable' + | 'Serp' + | 'Serverless' + | 'ServiceNow' + | 'Sftp' + | 'SharePointOnlineList' + | 'Shopify' + | 'Snowflake' + | 'Spark' + | 'SqlServer' + | 'Square' + | 'Sybase' + | 'Teradata' + | 'Vertica' + | 'WebTable' + | 'Xero' + | 'Zoho' + +type aadAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'AAD' +} + +type accessKeyAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'AccessKey' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionAccessKeyType +} + +type accountKeyAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'AccountKey' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionAccountKey +} + +type apiKeyAuthWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'ApiKey' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionApiKeyType +} + +type customKeysWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'CustomKeys' + + @description('Required. The credentials for the connection.') + credentials: customKeysType +} + +type managedIdentityAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'ManagedIdentity' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionManagedIdentityType +} + +type noneAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'None' +} + +type oauth2AuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'OAuth2' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionOAuth2Type +} + +type patAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'PAT' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionPersonalAccessTokenType +} + +type sasAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'SAS' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionSharedAccessSignatureType +} + +type servicePrincipalAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'ServicePrincipal' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionServicePrincipalType +} + +type usernamePasswordAuthTypeWorkspaceConnectionPropertyType = { + @description('Required. The authentication type of the connection target.') + authType: 'UsernamePassword' + + @description('Required. The credentials for the connection.') + credentials: workspaceConnectionUsernamePasswordType +} + +type customKeysType = { + @description('Required. The custom keys for the connection.') + keys: { + @description('Required. Key-value pairs for the custom keys.') + *: string + } +} + +type workspaceConnectionAccessKeyType = { + @description('Required. The connection access key ID.') + accessKeyId: string + + @description('Required. The connection secret access key.') + secretAccessKey: string +} + +type workspaceConnectionAccountKey = { + @description('Required. The connection key.') + key: string +} + +type workspaceConnectionApiKeyType = { + @description('Required. The connection API key.') + key: string +} + +type workspaceConnectionManagedIdentityType = { + @description('Required. The connection managed identity ID.') + clientId: string + + @description('Required. The connection managed identity resource ID.') + resourceId: string +} + +type workspaceConnectionOAuth2Type = { + @description('Conditional. The connection auth URL. Required by Concur connection category.') + authUrl: string? + + @minLength(36) + @maxLength(36) + @description('Required. The connection client ID in the format of UUID.') + clientId: string + + @description('Required. The connection client secret.') + clientSecret: string + + @description('Conditional. The connection developer token. Required by GoogleAdWords connection category.') + developerToken: string? + + @description('Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is \'Password\'.') + password: string? + + @description('Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories.') + refreshToken: string? + + @description('Required. The connection tenant ID. Required by QuickBooks and Xero connection categories.') + tenantId: string? + + @description('Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is \'Password\'.') + username: string? +} + +type workspaceConnectionPersonalAccessTokenType = { + @description('Required. The connection personal access token.') + pat: string +} + +type workspaceConnectionSharedAccessSignatureType = { + @description('Required. The connection SAS token.') + sas: string +} + +type workspaceConnectionServicePrincipalType = { + @description('Required. The connection client ID.') + clientId: string + + @description('Required. The connection client secret.') + clientSecret: string + + @description('Required. The connection tenant ID.') + tenantId: string +} + +type workspaceConnectionUsernamePasswordType = { + @description('Required. The connection password.') + password: string + + @description('Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to \'UsernamePassword\'.') + securityToken: string? + + @description('Required. The connection username.') + username: string +} + +@secure() +@export() +@discriminator('authType') +type connectionPropertyType = + | aadAuthTypeWorkspaceConnectionPropertyType + | accessKeyAuthTypeWorkspaceConnectionPropertyType + | apiKeyAuthWorkspaceConnectionPropertyType + | customKeysWorkspaceConnectionPropertyType + | managedIdentityAuthTypeWorkspaceConnectionPropertyType + | noneAuthTypeWorkspaceConnectionPropertyType + | oauth2AuthTypeWorkspaceConnectionPropertyType + | patAuthTypeWorkspaceConnectionPropertyType + | sasAuthTypeWorkspaceConnectionPropertyType + | servicePrincipalAuthTypeWorkspaceConnectionPropertyType + | usernamePasswordAuthTypeWorkspaceConnectionPropertyType diff --git a/avm/res/machine-learning-services/workspace/connection/main.json b/avm/res/machine-learning-services/workspace/connection/main.json new file mode 100644 index 0000000000..6e49f757aa --- /dev/null +++ b/avm/res/machine-learning-services/workspace/connection/main.json @@ -0,0 +1,723 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2277907099827503661" + }, + "name": "Machine Learning Services Workspaces Connections", + "description": "This module creates a connection in a Machine Learning Services workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "metadataType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. The metadata key-value pairs." + } + } + }, + "categoryType": { + "type": "string", + "allowedValues": [ + "ADLSGen2", + "AIServices", + "AmazonMws", + "AmazonRdsForOracle", + "AmazonRdsForSqlServer", + "AmazonRedshift", + "AmazonS3Compatible", + "ApiKey", + "AzureBlob", + "AzureDataExplorer", + "AzureDatabricksDeltaLake", + "AzureMariaDb", + "AzureMySqlDb", + "AzureOneLake", + "AzureOpenAI", + "AzurePostgresDb", + "AzureSqlDb", + "AzureSqlMi", + "AzureSynapseAnalytics", + "AzureTableStorage", + "BingLLMSearch", + "Cassandra", + "CognitiveSearch", + "CognitiveService", + "Concur", + "ContainerRegistry", + "CosmosDb", + "CosmosDbMongoDbApi", + "Couchbase", + "CustomKeys", + "Db2", + "Drill", + "Dynamics", + "DynamicsAx", + "DynamicsCrm", + "Eloqua", + "FileServer", + "FtpServer", + "GenericContainerRegistry", + "GenericHttp", + "GenericRest", + "Git", + "GoogleAdWords", + "GoogleBigQuery", + "GoogleCloudStorage", + "Greenplum", + "Hbase", + "Hdfs", + "Hive", + "Hubspot", + "Impala", + "Informix", + "Jira", + "Magento", + "MariaDb", + "Marketo", + "MicrosoftAccess", + "MongoDbAtlas", + "MongoDbV2", + "MySql", + "Netezza", + "ODataRest", + "Odbc", + "Office365", + "OpenAI", + "Oracle", + "OracleCloudStorage", + "OracleServiceCloud", + "PayPal", + "Phoenix", + "PostgreSql", + "Presto", + "PythonFeed", + "QuickBooks", + "Redis", + "Responsys", + "S3", + "Salesforce", + "SalesforceMarketingCloud", + "SalesforceServiceCloud", + "SapBw", + "SapCloudForCustomer", + "SapEcc", + "SapHana", + "SapOpenHub", + "SapTable", + "Serp", + "Serverless", + "ServiceNow", + "Sftp", + "SharePointOnlineList", + "Shopify", + "Snowflake", + "Spark", + "SqlServer", + "Square", + "Sybase", + "Teradata", + "Vertica", + "WebTable", + "Xero", + "Zoho" + ], + "metadata": { + "__bicep_export!": true + } + }, + "aadAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AAD" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + } + }, + "accessKeyAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AccessKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionAccessKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "accountKeyAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AccountKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionAccountKey", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "apiKeyAuthWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ApiKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionApiKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "customKeysWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "CustomKeys" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/customKeysType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "managedIdentityAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ManagedIdentity" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionManagedIdentityType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "noneAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "None" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + } + }, + "oauth2AuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "OAuth2" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionOAuth2Type", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "patAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "PAT" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionPersonalAccessTokenType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "sasAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "SAS" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionSharedAccessSignatureType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ServicePrincipal" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionServicePrincipalType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "UsernamePassword" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionUsernamePasswordType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "customKeysType": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. Key-value pairs for the custom keys." + } + }, + "metadata": { + "description": "Required. The custom keys for the connection." + } + } + } + }, + "workspaceConnectionAccessKeyType": { + "type": "object", + "properties": { + "accessKeyId": { + "type": "string", + "metadata": { + "description": "Required. The connection access key ID." + } + }, + "secretAccessKey": { + "type": "string", + "metadata": { + "description": "Required. The connection secret access key." + } + } + } + }, + "workspaceConnectionAccountKey": { + "type": "object", + "properties": { + "key": { + "type": "string", + "metadata": { + "description": "Required. The connection key." + } + } + } + }, + "workspaceConnectionApiKeyType": { + "type": "object", + "properties": { + "key": { + "type": "string", + "metadata": { + "description": "Required. The connection API key." + } + } + } + }, + "workspaceConnectionManagedIdentityType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity ID." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity resource ID." + } + } + } + }, + "workspaceConnectionOAuth2Type": { + "type": "object", + "properties": { + "authUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection auth URL. Required by Concur connection category." + } + }, + "clientId": { + "type": "string", + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Required. The connection client ID in the format of UUID." + } + }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "developerToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." + } + }, + "password": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + }, + "refreshToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." + } + }, + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + } + } + }, + "workspaceConnectionPersonalAccessTokenType": { + "type": "object", + "properties": { + "pat": { + "type": "string", + "metadata": { + "description": "Required. The connection personal access token." + } + } + } + }, + "workspaceConnectionSharedAccessSignatureType": { + "type": "object", + "properties": { + "sas": { + "type": "string", + "metadata": { + "description": "Required. The connection SAS token." + } + } + } + }, + "workspaceConnectionServicePrincipalType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection client ID." + } + }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The connection tenant ID." + } + } + } + }, + "workspaceConnectionUsernamePasswordType": { + "type": "object", + "properties": { + "password": { + "type": "string", + "metadata": { + "description": "Required. The connection password." + } + }, + "securityToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Required. The connection username." + } + } + } + }, + "connectionPropertyType": { + "type": "secureObject", + "discriminator": { + "propertyName": "authType", + "mapping": { + "AAD": { + "$ref": "#/definitions/aadAuthTypeWorkspaceConnectionPropertyType" + }, + "AccessKey": { + "$ref": "#/definitions/accessKeyAuthTypeWorkspaceConnectionPropertyType" + }, + "ApiKey": { + "$ref": "#/definitions/apiKeyAuthWorkspaceConnectionPropertyType" + }, + "CustomKeys": { + "$ref": "#/definitions/customKeysWorkspaceConnectionPropertyType" + }, + "ManagedIdentity": { + "$ref": "#/definitions/managedIdentityAuthTypeWorkspaceConnectionPropertyType" + }, + "None": { + "$ref": "#/definitions/noneAuthTypeWorkspaceConnectionPropertyType" + }, + "OAuth2": { + "$ref": "#/definitions/oauth2AuthTypeWorkspaceConnectionPropertyType" + }, + "PAT": { + "$ref": "#/definitions/patAuthTypeWorkspaceConnectionPropertyType" + }, + "SAS": { + "$ref": "#/definitions/sasAuthTypeWorkspaceConnectionPropertyType" + }, + "ServicePrincipal": { + "$ref": "#/definitions/servicePrincipalAuthTypeWorkspaceConnectionPropertyType" + }, + "UsernamePassword": { + "$ref": "#/definitions/usernamePasswordAuthTypeWorkspaceConnectionPropertyType" + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the connection to create." + } + }, + "machineLearningWorkspaceName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." + } + }, + "category": { + "$ref": "#/definitions/categoryType", + "metadata": { + "description": "Required. Category of the connection." + } + }, + "expiryTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The expiry time of the connection." + } + }, + "isSharedToAll": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether the connection is shared to all users in the workspace." + } + }, + "metadata": { + "$ref": "#/definitions/metadataType", + "defaultValue": {}, + "metadata": { + "description": "Optional. User metadata for the connection." + } + }, + "sharedUserList": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The shared user list of the connection." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target of the connection." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Value details of the workspace connection." + } + }, + "connectionProperties": { + "$ref": "#/definitions/connectionPropertyType", + "metadata": { + "description": "Required. The properties of the connection, specific to the auth type." + } + } + }, + "resources": { + "machineLearningWorkspace": { + "existing": true, + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('machineLearningWorkspaceName')]" + }, + "connection": { + "type": "Microsoft.MachineLearningServices/workspaces/connections", + "apiVersion": "2024-04-01", + "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", + "properties": "[union(createObject('category', parameters('category'), 'expiryTime', parameters('expiryTime'), 'isSharedToAll', parameters('isSharedToAll'), 'metadata', parameters('metadata'), 'sharedUserList', parameters('sharedUserList'), 'target', parameters('target'), 'value', parameters('value')), parameters('connectionProperties'))]", + "dependsOn": [ + "machineLearningWorkspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the connection." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/connections', parameters('machineLearningWorkspaceName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the connection." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the connection was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/machine-learning-services/workspace/main.bicep b/avm/res/machine-learning-services/workspace/main.bicep index 75a53dc8cc..6ff05fed5f 100644 --- a/avm/res/machine-learning-services/workspace/main.bicep +++ b/avm/res/machine-learning-services/workspace/main.bicep @@ -59,6 +59,9 @@ param privateEndpoints privateEndpointType @sys.description('Optional. Computes to create respectively attach to the workspace.') param computes array? +@sys.description('Optional. Connections to create in the workspace.') +param connections connectionType[] = [] + @sys.description('Optional. Resource tags.') param tags object? @@ -174,6 +177,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.machinelearningservices-workspace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -295,6 +309,24 @@ module workspace_computes 'compute/main.bicep' = [ } ] +module workspace_connections 'connection/main.bicep' = [ + for connection in connections: { + name: '${workspace.name}-${connection.name}-connection' + params: { + machineLearningWorkspaceName: workspace.name + name: connection.name + category: connection.category + expiryTime: connection.?expiryTime + isSharedToAll: connection.?isSharedToAll + metadata: connection.?metadata + sharedUserList: connection.?sharedUserList + target: connection.target + value: connection.?value + connectionProperties: connection.connectionProperties + } + } +] + resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { name: lock.?name ?? 'lock-${name}' properties: { @@ -335,7 +367,7 @@ resource workspace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 } ] -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.0' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -376,8 +408,7 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -389,14 +420,10 @@ module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. ] resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -448,6 +475,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -486,11 +516,20 @@ type privateEndpointType = { @sys.description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @sys.description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @sys.description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @sys.description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @sys.description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @sys.description('Optional. The name of the private DNS zone group config.') + name: string? - @sys.description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @sys.description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @sys.description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? @@ -700,3 +739,37 @@ type customerManagedKeyType = { @sys.description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') userAssignedIdentityResourceId: string? }? + +import { categoryType, connectionPropertyType } from 'connection/main.bicep' + +type connectionType = { + @sys.description('Required. Name of the connection to create.') + name: string + + @sys.description('Required. Category of the connection.') + category: categoryType + + @sys.description('Optional. The expiry time of the connection.') + expiryTime: string? + + @sys.description('Optional. Indicates whether the connection is shared to all users in the workspace.') + isSharedToAll: bool? + + @sys.description('Optional. User metadata for the connection.') + metadata: { + @sys.description('Required. The metadata key-value pairs.') + *: string + }? + + @sys.description('Optional. The shared user list of the connection.') + sharedUserList: string[]? + + @sys.description('Required. The target of the connection.') + target: string + + @sys.description('Optional. Value details of the workspace connection.') + value: string? + + @sys.description('Required. The properties of the connection, specific to the auth type.') + connectionProperties: connectionPropertyType +} diff --git a/avm/res/machine-learning-services/workspace/main.json b/avm/res/machine-learning-services/workspace/main.json index e6ad861acb..883c30b6d7 100644 --- a/avm/res/machine-learning-services/workspace/main.json +++ b/avm/res/machine-learning-services/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5554101983463248531" + "version": "0.29.47.4906", + "templateHash": "3070187757867486541" }, "name": "Machine Learning Services Workspaces", "description": "This module deploys a Machine Learning Services Workspace.", @@ -65,6 +65,13 @@ "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": { @@ -165,21 +172,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -747,203 +777,946 @@ } }, "nullable": true - } - }, - "parameters": { - "name": { - "type": "string", - "metadata": { - "description": "Required. The name of the machine learning workspace." - } }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for all resources." + "connectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the connection to create." + } + }, + "category": { + "$ref": "#/definitions/categoryType", + "metadata": { + "description": "Required. Category of the connection." + } + }, + "expiryTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The expiry time of the connection." + } + }, + "isSharedToAll": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether the connection is shared to all users in the workspace." + } + }, + "metadata": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. The metadata key-value pairs." + } + }, + "nullable": true, + "metadata": { + "description": "Optional. User metadata for the connection." + } + }, + "sharedUserList": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The shared user list of the connection." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target of the connection." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Value details of the workspace connection." + } + }, + "connectionProperties": { + "$ref": "#/definitions/connectionPropertyType", + "metadata": { + "description": "Required. The properties of the connection, specific to the auth type." + } + } } }, - "sku": { - "type": "string", - "allowedValues": [ - "Free", - "Basic", - "Standard", - "Premium" - ], + "_1.aadAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AAD" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + }, "metadata": { - "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "kind": { - "type": "string", - "defaultValue": "Default", - "allowedValues": [ - "Default", - "Project", - "Hub", - "FeatureStore" - ], + "_1.accessKeyAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AccessKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionAccessKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. The type of Azure Machine Learning workspace to create." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "associatedStorageAccountResourceId": { - "type": "string", - "nullable": true, + "_1.apiKeyAuthWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ApiKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionApiKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "associatedKeyVaultResourceId": { - "type": "string", - "nullable": true, + "_1.customKeysType": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. Key-value pairs for the custom keys." + } + }, + "metadata": { + "description": "Required. The custom keys for the connection." + } + } + }, "metadata": { - "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "associatedApplicationInsightsResourceId": { - "type": "string", - "nullable": true, + "_1.customKeysWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "CustomKeys" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.customKeysType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "associatedContainerRegistryResourceId": { - "type": "string", - "nullable": true, + "_1.managedIdentityAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ManagedIdentity" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionManagedIdentityType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. The resource ID of the associated Container Registry." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "lock": { - "$ref": "#/definitions/lockType", + "_1.noneAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "None" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + }, "metadata": { - "description": "Optional. The lock settings of the service." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "hbiWorkspace": { - "type": "bool", - "defaultValue": false, + "_1.oauth2AuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "OAuth2" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionOAuth2Type", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "hubResourceId": { - "type": "string", - "nullable": true, + "_1.patAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "PAT" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionPersonalAccessTokenType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "roleAssignments": { - "$ref": "#/definitions/roleAssignmentType", + "_1.sasAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "SAS" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionSharedAccessSignatureType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. Array of role assignments to create." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "privateEndpoints": { - "$ref": "#/definitions/privateEndpointType", + "_1.servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ServicePrincipal" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionServicePrincipalType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "computes": { - "type": "array", - "nullable": true, + "_1.usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "UsernamePassword" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/_1.workspaceConnectionUsernamePasswordType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + }, "metadata": { - "description": "Optional. Computes to create respectively attach to the workspace." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "tags": { + "_1.workspaceConnectionAccessKeyType": { "type": "object", - "nullable": true, + "properties": { + "accessKeyId": { + "type": "string", + "metadata": { + "description": "Required. The connection access key ID." + } + }, + "secretAccessKey": { + "type": "string", + "metadata": { + "description": "Required. The connection secret access key." + } + } + }, "metadata": { - "description": "Optional. Resource tags." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "enableTelemetry": { - "type": "bool", - "defaultValue": true, + "_1.workspaceConnectionApiKeyType": { + "type": "object", + "properties": { + "key": { + "type": "string", + "metadata": { + "description": "Required. The connection API key." + } + } + }, "metadata": { - "description": "Optional. Enable/Disable usage telemetry for module." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentitiesType", - "defaultValue": { - "systemAssigned": true + "_1.workspaceConnectionManagedIdentityType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity ID." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity resource ID." + } + } }, "metadata": { - "description": "Optional. The managed identity definition for this resource. At least one identity type is required." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "featureStoreSettings": { - "$ref": "#/definitions/featureStoreSettingType", + "_1.workspaceConnectionOAuth2Type": { + "type": "object", + "properties": { + "authUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection auth URL. Required by Concur connection category." + } + }, + "clientId": { + "type": "string", + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Required. The connection client ID in the format of UUID." + } + }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "developerToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." + } + }, + "password": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + }, + "refreshToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." + } + }, + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + } + }, "metadata": { - "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "managedNetworkSettings": { - "$ref": "#/definitions/managedNetworkSettingType", + "_1.workspaceConnectionPersonalAccessTokenType": { + "type": "object", + "properties": { + "pat": { + "type": "string", + "metadata": { + "description": "Required. The connection personal access token." + } + } + }, "metadata": { - "description": "Optional. Managed Network settings for a machine learning workspace." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "serverlessComputeSettings": { - "$ref": "#/definitions/serverlessComputeSettingType", + "_1.workspaceConnectionServicePrincipalType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection client ID." + } + }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The connection tenant ID." + } + } + }, "metadata": { - "description": "Optional. Settings for serverless compute created in the workspace." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "systemDatastoresAuthMode": { - "type": "string", - "nullable": true, - "allowedValues": [ - "accessKey", - "identity" - ], + "_1.workspaceConnectionSharedAccessSignatureType": { + "type": "object", + "properties": { + "sas": { + "type": "string", + "metadata": { + "description": "Required. The connection SAS token." + } + } + }, "metadata": { - "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "workspaceHubConfig": { - "$ref": "#/definitions/workspaceHubConfigType", + "_1.workspaceConnectionUsernamePasswordType": { + "type": "object", + "properties": { + "password": { + "type": "string", + "metadata": { + "description": "Required. The connection password." + } + }, + "securityToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Required. The connection username." + } + } + }, "metadata": { - "description": "Optional. Configuration for workspace hub settings." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "diagnosticSettings": { - "$ref": "#/definitions/diagnosticSettingType", + "categoryType": { + "type": "string", + "allowedValues": [ + "ADLSGen2", + "AIServices", + "AmazonMws", + "AmazonRdsForOracle", + "AmazonRdsForSqlServer", + "AmazonRedshift", + "AmazonS3Compatible", + "ApiKey", + "AzureBlob", + "AzureDataExplorer", + "AzureDatabricksDeltaLake", + "AzureMariaDb", + "AzureMySqlDb", + "AzureOneLake", + "AzureOpenAI", + "AzurePostgresDb", + "AzureSqlDb", + "AzureSqlMi", + "AzureSynapseAnalytics", + "AzureTableStorage", + "BingLLMSearch", + "Cassandra", + "CognitiveSearch", + "CognitiveService", + "Concur", + "ContainerRegistry", + "CosmosDb", + "CosmosDbMongoDbApi", + "Couchbase", + "CustomKeys", + "Db2", + "Drill", + "Dynamics", + "DynamicsAx", + "DynamicsCrm", + "Eloqua", + "FileServer", + "FtpServer", + "GenericContainerRegistry", + "GenericHttp", + "GenericRest", + "Git", + "GoogleAdWords", + "GoogleBigQuery", + "GoogleCloudStorage", + "Greenplum", + "Hbase", + "Hdfs", + "Hive", + "Hubspot", + "Impala", + "Informix", + "Jira", + "Magento", + "MariaDb", + "Marketo", + "MicrosoftAccess", + "MongoDbAtlas", + "MongoDbV2", + "MySql", + "Netezza", + "ODataRest", + "Odbc", + "Office365", + "OpenAI", + "Oracle", + "OracleCloudStorage", + "OracleServiceCloud", + "PayPal", + "Phoenix", + "PostgreSql", + "Presto", + "PythonFeed", + "QuickBooks", + "Redis", + "Responsys", + "S3", + "Salesforce", + "SalesforceMarketingCloud", + "SalesforceServiceCloud", + "SapBw", + "SapCloudForCustomer", + "SapEcc", + "SapHana", + "SapOpenHub", + "SapTable", + "Serp", + "Serverless", + "ServiceNow", + "Sftp", + "SharePointOnlineList", + "Shopify", + "Snowflake", + "Spark", + "SqlServer", + "Square", + "Sybase", + "Teradata", + "Vertica", + "WebTable", + "Xero", + "Zoho" + ], "metadata": { - "description": "Optional. The diagnostic settings of the service." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } }, - "description": { - "type": "string", - "nullable": true, + "connectionPropertyType": { + "type": "secureObject", + "discriminator": { + "propertyName": "authType", + "mapping": { + "AAD": { + "$ref": "#/definitions/_1.aadAuthTypeWorkspaceConnectionPropertyType" + }, + "AccessKey": { + "$ref": "#/definitions/_1.accessKeyAuthTypeWorkspaceConnectionPropertyType" + }, + "ApiKey": { + "$ref": "#/definitions/_1.apiKeyAuthWorkspaceConnectionPropertyType" + }, + "CustomKeys": { + "$ref": "#/definitions/_1.customKeysWorkspaceConnectionPropertyType" + }, + "ManagedIdentity": { + "$ref": "#/definitions/_1.managedIdentityAuthTypeWorkspaceConnectionPropertyType" + }, + "None": { + "$ref": "#/definitions/_1.noneAuthTypeWorkspaceConnectionPropertyType" + }, + "OAuth2": { + "$ref": "#/definitions/_1.oauth2AuthTypeWorkspaceConnectionPropertyType" + }, + "PAT": { + "$ref": "#/definitions/_1.patAuthTypeWorkspaceConnectionPropertyType" + }, + "SAS": { + "$ref": "#/definitions/_1.sasAuthTypeWorkspaceConnectionPropertyType" + }, + "ServicePrincipal": { + "$ref": "#/definitions/_1.servicePrincipalAuthTypeWorkspaceConnectionPropertyType" + }, + "UsernamePassword": { + "$ref": "#/definitions/_1.usernamePasswordAuthTypeWorkspaceConnectionPropertyType" + } + } + }, "metadata": { - "description": "Optional. The description of this workspace." + "__bicep_imported_from!": { + "sourceTemplate": "connection/main.bicep" + } } - }, - "discoveryUrl": { + } + }, + "parameters": { + "name": { "type": "string", - "nullable": true, "metadata": { - "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." + "description": "Required. The name of the machine learning workspace." } }, - "customerManagedKey": { - "$ref": "#/definitions/customerManagedKeyType", + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. The customer managed key definition." + "description": "Optional. Location for all resources." } }, - "imageBuildCompute": { + "sku": { "type": "string", - "nullable": true, + "allowedValues": [ + "Free", + "Basic", + "Standard", + "Premium" + ], "metadata": { - "description": "Optional. The compute name for image build." + "description": "Required. Specifies the SKU, also referred as 'edition' of the Azure Machine Learning workspace." + } + }, + "kind": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "Project", + "Hub", + "FeatureStore" + ], + "metadata": { + "description": "Optional. The type of Azure Machine Learning workspace to create." + } + }, + "associatedStorageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Storage Account. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedKeyVaultResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Key Vault. Required if 'kind' is 'Default', 'FeatureStore' or 'Hub'." + } + }, + "associatedApplicationInsightsResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the associated Application Insights. Required if 'kind' is 'Default' or 'FeatureStore'." + } + }, + "associatedContainerRegistryResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the associated Container Registry." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "hbiWorkspace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to signal HBI data in the workspace and reduce diagnostic data collected by the service." + } + }, + "hubResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The resource ID of the hub to associate with the workspace. Required if 'kind' is set to 'Project'." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "computes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Computes to create respectively attach to the workspace." + } + }, + "connections": { + "type": "array", + "items": { + "$ref": "#/definitions/connectionType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Connections to create in the workspace." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "defaultValue": { + "systemAssigned": true + }, + "metadata": { + "description": "Optional. The managed identity definition for this resource. At least one identity type is required." + } + }, + "featureStoreSettings": { + "$ref": "#/definitions/featureStoreSettingType", + "metadata": { + "description": "Conditional. Settings for feature store type workspaces. Required if 'kind' is set to 'FeatureStore'." + } + }, + "managedNetworkSettings": { + "$ref": "#/definitions/managedNetworkSettingType", + "metadata": { + "description": "Optional. Managed Network settings for a machine learning workspace." + } + }, + "serverlessComputeSettings": { + "$ref": "#/definitions/serverlessComputeSettingType", + "metadata": { + "description": "Optional. Settings for serverless compute created in the workspace." + } + }, + "systemDatastoresAuthMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "accessKey", + "identity" + ], + "metadata": { + "description": "Optional. The authentication mode used by the workspace when connecting to the default storage account." + } + }, + "workspaceHubConfig": { + "$ref": "#/definitions/workspaceHubConfigType", + "metadata": { + "description": "Optional. Configuration for workspace hub settings." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of this workspace." + } + }, + "discoveryUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. URL for the discovery service to identify regional endpoints for machine learning experimentation services." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "imageBuildCompute": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The compute name for image build." } }, "primaryUserAssignedIdentity": { @@ -980,6 +1753,13 @@ } }, "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": { @@ -1121,20 +1901,20 @@ "workspace_roleAssignments": { "copy": { "name": "workspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.MachineLearningServices/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "workspace" @@ -1201,151 +1981,944 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16086831886520789699" + "version": "0.29.47.4906", + "templateHash": "8580750401363518569" + }, + "name": "Machine Learning Services Workspaces Computes", + "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "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." + } + } + }, + "nullable": true + } + }, + "parameters": { + "machineLearningWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "minLength": 2, + "maxLength": 16, + "metadata": { + "description": "Required. Name of the compute." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Specifies the location of the resource." + } + }, + "sku": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Basic", + "Free", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "deployCompute": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." + } + }, + "computeLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the Machine Learning compute." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. ARM resource ID of the underlying compute." + } + }, + "computeType": { + "type": "string", + "allowedValues": [ + "AKS", + "AmlCompute", + "ComputeInstance", + "Databricks", + "DataFactory", + "DataLakeAnalytics", + "HDInsight", + "Kubernetes", + "SynapseSpark", + "VirtualMachine" + ], + "metadata": { + "description": "Required. Set the object type." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "machineLearningWorkspace": { + "existing": true, + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('machineLearningWorkspaceName')]" + }, + "compute": { + "condition": "[equals(parameters('deployCompute'), true())]", + "type": "Microsoft.MachineLearningServices/workspaces/computes", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", + "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", + "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", + "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]", + "dependsOn": [ + "machineLearningWorkspace" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the compute." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the compute." + }, + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the compute was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('compute', '2022-10-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "workspace", + "workspace_privateEndpoints" + ] + }, + "workspace_connections": { + "copy": { + "name": "workspace_connections", + "count": "[length(parameters('connections'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-connection', parameters('name'), parameters('connections')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "machineLearningWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('connections')[copyIndex()].name]" + }, + "category": { + "value": "[parameters('connections')[copyIndex()].category]" + }, + "expiryTime": { + "value": "[tryGet(parameters('connections')[copyIndex()], 'expiryTime')]" + }, + "isSharedToAll": { + "value": "[tryGet(parameters('connections')[copyIndex()], 'isSharedToAll')]" + }, + "metadata": { + "value": "[tryGet(parameters('connections')[copyIndex()], 'metadata')]" + }, + "sharedUserList": { + "value": "[tryGet(parameters('connections')[copyIndex()], 'sharedUserList')]" + }, + "target": { + "value": "[parameters('connections')[copyIndex()].target]" + }, + "value": { + "value": "[tryGet(parameters('connections')[copyIndex()], 'value')]" + }, + "connectionProperties": { + "value": "[parameters('connections')[copyIndex()].connectionProperties]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2277907099827503661" + }, + "name": "Machine Learning Services Workspaces Connections", + "description": "This module creates a connection in a Machine Learning Services workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "metadataType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. The metadata key-value pairs." + } + } + }, + "categoryType": { + "type": "string", + "allowedValues": [ + "ADLSGen2", + "AIServices", + "AmazonMws", + "AmazonRdsForOracle", + "AmazonRdsForSqlServer", + "AmazonRedshift", + "AmazonS3Compatible", + "ApiKey", + "AzureBlob", + "AzureDataExplorer", + "AzureDatabricksDeltaLake", + "AzureMariaDb", + "AzureMySqlDb", + "AzureOneLake", + "AzureOpenAI", + "AzurePostgresDb", + "AzureSqlDb", + "AzureSqlMi", + "AzureSynapseAnalytics", + "AzureTableStorage", + "BingLLMSearch", + "Cassandra", + "CognitiveSearch", + "CognitiveService", + "Concur", + "ContainerRegistry", + "CosmosDb", + "CosmosDbMongoDbApi", + "Couchbase", + "CustomKeys", + "Db2", + "Drill", + "Dynamics", + "DynamicsAx", + "DynamicsCrm", + "Eloqua", + "FileServer", + "FtpServer", + "GenericContainerRegistry", + "GenericHttp", + "GenericRest", + "Git", + "GoogleAdWords", + "GoogleBigQuery", + "GoogleCloudStorage", + "Greenplum", + "Hbase", + "Hdfs", + "Hive", + "Hubspot", + "Impala", + "Informix", + "Jira", + "Magento", + "MariaDb", + "Marketo", + "MicrosoftAccess", + "MongoDbAtlas", + "MongoDbV2", + "MySql", + "Netezza", + "ODataRest", + "Odbc", + "Office365", + "OpenAI", + "Oracle", + "OracleCloudStorage", + "OracleServiceCloud", + "PayPal", + "Phoenix", + "PostgreSql", + "Presto", + "PythonFeed", + "QuickBooks", + "Redis", + "Responsys", + "S3", + "Salesforce", + "SalesforceMarketingCloud", + "SalesforceServiceCloud", + "SapBw", + "SapCloudForCustomer", + "SapEcc", + "SapHana", + "SapOpenHub", + "SapTable", + "Serp", + "Serverless", + "ServiceNow", + "Sftp", + "SharePointOnlineList", + "Shopify", + "Snowflake", + "Spark", + "SqlServer", + "Square", + "Sybase", + "Teradata", + "Vertica", + "WebTable", + "Xero", + "Zoho" + ], + "metadata": { + "__bicep_export!": true + } + }, + "aadAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AAD" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + } + }, + "accessKeyAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AccessKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionAccessKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "accountKeyAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "AccountKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionAccountKey", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "apiKeyAuthWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ApiKey" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionApiKeyType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "customKeysWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "CustomKeys" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/customKeysType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "managedIdentityAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ManagedIdentity" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionManagedIdentityType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "noneAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "None" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + } + } + }, + "oauth2AuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "OAuth2" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionOAuth2Type", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "patAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "PAT" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionPersonalAccessTokenType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "sasAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "SAS" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionSharedAccessSignatureType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "servicePrincipalAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "ServicePrincipal" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionServicePrincipalType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "usernamePasswordAuthTypeWorkspaceConnectionPropertyType": { + "type": "object", + "properties": { + "authType": { + "type": "string", + "allowedValues": [ + "UsernamePassword" + ], + "metadata": { + "description": "Required. The authentication type of the connection target." + } + }, + "credentials": { + "$ref": "#/definitions/workspaceConnectionUsernamePasswordType", + "metadata": { + "description": "Required. The credentials for the connection." + } + } + } + }, + "customKeysType": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "string", + "metadata": { + "description": "Required. Key-value pairs for the custom keys." + } + }, + "metadata": { + "description": "Required. The custom keys for the connection." + } + } + } + }, + "workspaceConnectionAccessKeyType": { + "type": "object", + "properties": { + "accessKeyId": { + "type": "string", + "metadata": { + "description": "Required. The connection access key ID." + } + }, + "secretAccessKey": { + "type": "string", + "metadata": { + "description": "Required. The connection secret access key." + } + } + } + }, + "workspaceConnectionAccountKey": { + "type": "object", + "properties": { + "key": { + "type": "string", + "metadata": { + "description": "Required. The connection key." + } + } + } + }, + "workspaceConnectionApiKeyType": { + "type": "object", + "properties": { + "key": { + "type": "string", + "metadata": { + "description": "Required. The connection API key." + } + } + } + }, + "workspaceConnectionManagedIdentityType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity ID." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The connection managed identity resource ID." + } + } + } + }, + "workspaceConnectionOAuth2Type": { + "type": "object", + "properties": { + "authUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection auth URL. Required by Concur connection category." + } + }, + "clientId": { + "type": "string", + "minLength": 36, + "maxLength": 36, + "metadata": { + "description": "Required. The connection client ID in the format of UUID." + } + }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "developerToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection developer token. Required by GoogleAdWords connection category." + } + }, + "password": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection password. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + }, + "refreshToken": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection refresh token. Required by GoogleBigQuery, GoogleAdWords, Hubspot, QuickBooks, Square, Xero and Zoho connection categories." + } + }, + "tenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The connection tenant ID. Required by QuickBooks and Xero connection categories." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The connection username. Required by Concur and ServiceNow connection categories where AccessToken grant type is 'Password'." + } + } + } }, - "name": "Machine Learning Services Workspaces Computes", - "description": "This module deploys a Machine Learning Services Workspaces Compute.\n\nAttaching a compute is not idempotent and will fail in case you try to redeploy over an existing compute in AML (see parameter `deployCompute`).", - "owner": "Azure/module-maintainers" - }, - "definitions": { - "managedIdentitiesType": { + "workspaceConnectionPersonalAccessTokenType": { "type": "object", "properties": { - "systemAssigned": { - "type": "bool", - "nullable": true, + "pat": { + "type": "string", "metadata": { - "description": "Optional. Enables system assigned managed identity on the resource." + "description": "Required. The connection personal access token." + } + } + } + }, + "workspaceConnectionSharedAccessSignatureType": { + "type": "object", + "properties": { + "sas": { + "type": "string", + "metadata": { + "description": "Required. The connection SAS token." + } + } + } + }, + "workspaceConnectionServicePrincipalType": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "metadata": { + "description": "Required. The connection client ID." } }, - "userAssignedResourceIds": { - "type": "array", - "items": { - "type": "string" - }, + "clientSecret": { + "type": "string", + "metadata": { + "description": "Required. The connection client secret." + } + }, + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The connection tenant ID." + } + } + } + }, + "workspaceConnectionUsernamePasswordType": { + "type": "object", + "properties": { + "password": { + "type": "string", + "metadata": { + "description": "Required. The connection password." + } + }, + "securityToken": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The resource ID(s) to assign to the resource." + "description": "Conditional. The connection security token. Required by connections like SalesForce for extra security in addition to 'UsernamePassword'." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Required. The connection username." + } + } + } + }, + "connectionPropertyType": { + "type": "secureObject", + "discriminator": { + "propertyName": "authType", + "mapping": { + "AAD": { + "$ref": "#/definitions/aadAuthTypeWorkspaceConnectionPropertyType" + }, + "AccessKey": { + "$ref": "#/definitions/accessKeyAuthTypeWorkspaceConnectionPropertyType" + }, + "ApiKey": { + "$ref": "#/definitions/apiKeyAuthWorkspaceConnectionPropertyType" + }, + "CustomKeys": { + "$ref": "#/definitions/customKeysWorkspaceConnectionPropertyType" + }, + "ManagedIdentity": { + "$ref": "#/definitions/managedIdentityAuthTypeWorkspaceConnectionPropertyType" + }, + "None": { + "$ref": "#/definitions/noneAuthTypeWorkspaceConnectionPropertyType" + }, + "OAuth2": { + "$ref": "#/definitions/oauth2AuthTypeWorkspaceConnectionPropertyType" + }, + "PAT": { + "$ref": "#/definitions/patAuthTypeWorkspaceConnectionPropertyType" + }, + "SAS": { + "$ref": "#/definitions/sasAuthTypeWorkspaceConnectionPropertyType" + }, + "ServicePrincipal": { + "$ref": "#/definitions/servicePrincipalAuthTypeWorkspaceConnectionPropertyType" + }, + "UsernamePassword": { + "$ref": "#/definitions/usernamePasswordAuthTypeWorkspaceConnectionPropertyType" } } }, - "nullable": true + "metadata": { + "__bicep_export!": true + } } }, "parameters": { - "machineLearningWorkspaceName": { - "type": "string", - "metadata": { - "description": "Conditional. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." - } - }, "name": { "type": "string", - "minLength": 2, - "maxLength": 16, "metadata": { - "description": "Required. Name of the compute." + "description": "Required. Name of the connection to create." } }, - "location": { + "machineLearningWorkspaceName": { "type": "string", - "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Specifies the location of the resource." + "description": "Required. The name of the parent Machine Learning Workspace. Required if the template is used in a standalone deployment." } }, - "sku": { - "type": "string", - "nullable": true, - "allowedValues": [ - "Basic", - "Free", - "Premium", - "Standard" - ], + "category": { + "$ref": "#/definitions/categoryType", "metadata": { - "description": "Optional. Specifies the sku, also referred as \"edition\". Required for creating a compute resource." + "description": "Required. Category of the connection." } }, - "tags": { - "type": "object", + "expiryTime": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. Contains resource tags defined as key-value pairs. Ignored when attaching a compute resource, i.e. when you provide a resource ID." + "description": "Optional. The expiry time of the connection." } }, - "deployCompute": { + "isSharedToAll": { "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Flag to specify whether to deploy the compute. Required only for attach (i.e. providing a resource ID), as in this case the operation is not idempotent, i.e. a second deployment will fail. Therefore, this flag needs to be set to \"false\" as long as the compute resource exists." - } - }, - "computeLocation": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Optional. Location for the underlying compute. Ignored when attaching a compute resource, i.e. when you provide a resource ID." - } - }, - "description": { - "type": "string", "nullable": true, "metadata": { - "description": "Optional. The description of the Machine Learning compute." + "description": "Optional. Indicates whether the connection is shared to all users in the workspace." } }, - "disableLocalAuth": { - "type": "bool", - "defaultValue": false, + "metadata": { + "$ref": "#/definitions/metadataType", + "defaultValue": {}, "metadata": { - "description": "Optional. Opt-out of local authentication and ensure customers can use only MSI and AAD exclusively for authentication." + "description": "Optional. User metadata for the connection." } }, - "resourceId": { - "type": "string", + "sharedUserList": { + "type": "array", + "items": { + "type": "string" + }, "nullable": true, "metadata": { - "description": "Optional. ARM resource ID of the underlying compute." + "description": "Optional. The shared user list of the connection." } }, - "computeType": { + "target": { "type": "string", - "allowedValues": [ - "AKS", - "AmlCompute", - "ComputeInstance", - "Databricks", - "DataFactory", - "DataLakeAnalytics", - "HDInsight", - "Kubernetes", - "SynapseSpark", - "VirtualMachine" - ], "metadata": { - "description": "Required. Set the object type." + "description": "Required. The target of the connection." } }, - "properties": { - "type": "object", + "value": { + "type": "string", "nullable": true, "metadata": { - "description": "Optional. The properties of the compute. Will be ignored in case \"resourceId\" is set." + "description": "Optional. Value details of the workspace connection." } }, - "managedIdentities": { - "$ref": "#/definitions/managedIdentitiesType", + "connectionProperties": { + "$ref": "#/definitions/connectionPropertyType", "metadata": { - "description": "Optional. The managed identity definition for this resource." + "description": "Required. The properties of the connection, specific to the auth type." } } }, - "variables": { - "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" - }, "resources": { "machineLearningWorkspace": { "existing": true, @@ -1353,63 +2926,43 @@ "apiVersion": "2022-10-01", "name": "[parameters('machineLearningWorkspaceName')]" }, - "compute": { - "condition": "[equals(parameters('deployCompute'), true())]", - "type": "Microsoft.MachineLearningServices/workspaces/computes", - "apiVersion": "2022-10-01", + "connection": { + "type": "Microsoft.MachineLearningServices/workspaces/connections", + "apiVersion": "2024-04-01", "name": "[format('{0}/{1}', parameters('machineLearningWorkspaceName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[if(empty(parameters('resourceId')), parameters('tags'), null())]", - "sku": "[if(empty(parameters('resourceId')), createObject('name', parameters('sku'), 'tier', parameters('sku')), null())]", - "identity": "[if(empty(parameters('resourceId')), variables('identity'), null())]", - "properties": "[union(createObject('description', parameters('description'), 'disableLocalAuth', parameters('disableLocalAuth'), 'computeType', parameters('computeType')), if(not(empty(parameters('resourceId'))), createObject('resourceId', parameters('resourceId')), createObject('computeLocation', parameters('computeLocation'), 'properties', parameters('properties'))))]", + "properties": "[union(createObject('category', parameters('category'), 'expiryTime', parameters('expiryTime'), 'isSharedToAll', parameters('isSharedToAll'), 'metadata', parameters('metadata'), 'sharedUserList', parameters('sharedUserList'), 'target', parameters('target'), 'value', parameters('value')), parameters('connectionProperties'))]", "dependsOn": [ "machineLearningWorkspace" ] } }, "outputs": { - "name": { + "resourceId": { "type": "string", "metadata": { - "description": "The name of the compute." + "description": "The resource ID of the connection." }, - "value": "[parameters('name')]" + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/connections', parameters('machineLearningWorkspaceName'), parameters('name'))]" }, - "resourceId": { + "name": { "type": "string", "metadata": { - "description": "The resource ID of the compute." + "description": "The name of the connection." }, - "value": "[resourceId('Microsoft.MachineLearningServices/workspaces/computes', parameters('machineLearningWorkspaceName'), parameters('name'))]" + "value": "[parameters('name')]" }, "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the compute was deployed into." + "description": "The name of the resource group the connection was created in." }, "value": "[resourceGroup().name]" - }, - "systemAssignedMIPrincipalId": { - "type": "string", - "metadata": { - "description": "The principal ID of the system assigned identity." - }, - "value": "[coalesce(tryGet(tryGet(reference('compute', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" - }, - "location": { - "type": "string", - "metadata": { - "description": "The location the resource was deployed into." - }, - "value": "[reference('compute', '2022-10-01', 'full').location]" } } } }, "dependsOn": [ - "workspace", - "workspace_privateEndpoints" + "workspace" ] }, "workspace_privateEndpoints": { @@ -1444,11 +2997,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1476,19 +3026,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "3308873178893851812" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1592,13 +3170,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1632,8 +3210,11 @@ "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." + "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": { @@ -1673,8 +3254,11 @@ "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." + "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": { @@ -1722,6 +3306,29 @@ } }, "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": { @@ -1757,18 +3364,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1824,6 +3424,13 @@ } }, "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')]", @@ -1841,8 +3448,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1860,7 +3467,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1901,27 +3508,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1932,28 +3539,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1961,12 +3592,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1980,27 +3614,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2058,14 +3701,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/ai/dependencies.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/ai/dependencies.bicep index 8ea028e953..9a87497d4f 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/ai/dependencies.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/ai/dependencies.bicep @@ -13,6 +13,9 @@ param storageAccountName string @description('Required. The name of the additional Storage Account to create.') param secondaryStorageAccountName string +@description('Required. The name of the AI Services to create.') +param aiServicesName string + resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: keyVaultName location: location @@ -56,6 +59,15 @@ resource secondaryStorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' kind: 'StorageV2' } +resource aiServices 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: aiServicesName + location: location + sku: { + name: 'S0' + } + kind: 'AIServices' +} + @description('The resource ID of the created Application Insights instance.') output applicationInsightsResourceId string = applicationInsights.id @@ -67,3 +79,12 @@ output secondaryStorageAccountResourceId string = secondaryStorageAccount.id @description('The resource ID of the created Key Vault.') output keyVaultResourceId string = keyVault.id + +@description('The resource ID of the created AI Services.') +output aiServicesResourceId string = aiServices.id + +@description('The main endpoint of the created AI Services.') +output aiServicesEndpoint string = aiServices.properties.endpoint + +@description('The endpoints of the created AI Services.') +output aiServicesEndpoints object = aiServices.properties.endpoints diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep index dea496d17f..8ea4b81b79 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep @@ -42,6 +42,7 @@ module nestedDependencies 'dependencies.bicep' = { applicationInsightsName: 'dep-${namePrefix}-appI-${serviceShort}' storageAccountName: 'dep${namePrefix}st${serviceShort}' secondaryStorageAccountName: 'dep${namePrefix}st${serviceShort}2' + aiServicesName: 'dep-${namePrefix}-ai-${serviceShort}' location: resourceLocation } } @@ -63,6 +64,26 @@ module testDeployment '../../../main.bicep' = [ associatedStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId sku: 'Basic' kind: 'Hub' + connections: [ + { + name: 'ai' + category: 'AIServices' + target: nestedDependencies.outputs.aiServicesEndpoint + connectionProperties: { + authType: 'ApiKey' + credentials: { + key: 'key' + } + } + metadata: { + ApiType: 'Azure' + ResourceId: nestedDependencies.outputs.aiServicesResourceId + Location: resourceLocation + ApiVersion: '2023-07-01-preview' + DeploymentApiVersion: '2023-10-01-preview' + } + } + ] workspaceHubConfig: { additionalWorkspaceStorageAccounts: [nestedDependencies.outputs.secondaryStorageAccountResourceId] defaultWorkspaceResourceGroup: resourceGroup.id diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/max/main.test.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/max/main.test.bicep index b1fff69995..56119cb3d1 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/max/main.test.bicep @@ -108,6 +108,19 @@ module testDeployment '../../../main.bicep' = [ } } ] + connections: [ + { + name: 'connection' + category: 'ApiKey' + target: 'https://example.com' + connectionProperties: { + authType: 'ApiKey' + credentials: { + key: 'key' + } + } + } + ] description: 'The cake is a lie.' diagnosticSettings: [ { @@ -133,9 +146,15 @@ module testDeployment '../../../main.bicep' = [ privateEndpoints: [ { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + name: 'group1' + privateDnsZoneGroupConfigs: [ + { + name: 'config1' + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -144,18 +163,26 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + name: 'group2' + privateDnsZoneGroupConfigs: [ + { + name: 'config2' + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] roleAssignments: [ { + name: 'f9b5b0d9-f27e-4c89-bacf-1bbc4a99dbce' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep index 9d3e13cb35..6361689d2e 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/waf-aligned/main.test.bicep @@ -88,9 +88,13 @@ module testDeployment '../../../main.bicep' = [ privateEndpoints: [ { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/machine-learning-services/workspace/version.json b/avm/res/machine-learning-services/workspace/version.json index 96236a61ba..a89e5c9d3c 100644 --- a/avm/res/machine-learning-services/workspace/version.json +++ b/avm/res/machine-learning-services/workspace/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", - "pathFilters": [ - "./main.json" - ] + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.7", + "pathFilters": [ + "./main.json" + ] } diff --git a/avm/res/maintenance/maintenance-configuration/README.md b/avm/res/maintenance/maintenance-configuration/README.md index 777e0b0848..efbe74fd63 100644 --- a/avm/res/maintenance/maintenance-configuration/README.md +++ b/avm/res/maintenance/maintenance-configuration/README.md @@ -130,11 +130,13 @@ module maintenanceConfiguration 'br/public:avm/res/maintenance/maintenance-confi namespace: 'mmcmaxns' roleAssignments: [ { + name: 'd78ec5f7-4692-4f43-8c17-7569466bbed5' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -222,11 +224,13 @@ module maintenanceConfiguration 'br/public:avm/res/maintenance/maintenance-confi "roleAssignments": { "value": [ { + "name": "d78ec5f7-4692-4f43-8c17-7569466bbed5", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -542,6 +546,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -592,6 +597,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/maintenance/maintenance-configuration/main.bicep b/avm/res/maintenance/maintenance-configuration/main.bicep index 00950bbda9..da1590a6bc 100644 --- a/avm/res/maintenance/maintenance-configuration/main.bicep +++ b/avm/res/maintenance/maintenance-configuration/main.bicep @@ -77,6 +77,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.maintenance-maintenanceconfiguration.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -122,14 +133,14 @@ resource maintenanceConfiguration_lock 'Microsoft.Authorization/locks@2020-05-01 } resource maintenanceConfiguration_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(maintenanceConfiguration.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + maintenanceConfiguration.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -170,6 +181,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/maintenance/maintenance-configuration/main.json b/avm/res/maintenance/maintenance-configuration/main.json index 31efb4461b..651c20c00f 100644 --- a/avm/res/maintenance/maintenance-configuration/main.json +++ b/avm/res/maintenance/maintenance-configuration/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13929983943775275040" + "version": "0.29.47.4906", + "templateHash": "9188458421094591722" }, "name": "Maintenance Configurations", "description": "This module deploys a Maintenance Configuration.", @@ -43,6 +43,13 @@ "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": { @@ -202,6 +209,13 @@ } }, "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')]", @@ -264,20 +278,20 @@ "maintenanceConfiguration_roleAssignments": { "copy": { "name": "maintenanceConfiguration_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Maintenance/maintenanceConfigurations/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Maintenance/maintenanceConfigurations', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/maintenance/maintenance-configuration/tests/e2e/max/main.test.bicep b/avm/res/maintenance/maintenance-configuration/tests/e2e/max/main.test.bicep index c794755eb8..7ece376e18 100644 --- a/avm/res/maintenance/maintenance-configuration/tests/e2e/max/main.test.bicep +++ b/avm/res/maintenance/maintenance-configuration/tests/e2e/max/main.test.bicep @@ -66,11 +66,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'd78ec5f7-4692-4f43-8c17-7569466bbed5' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/maintenance/maintenance-configuration/version.json b/avm/res/maintenance/maintenance-configuration/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/maintenance/maintenance-configuration/version.json +++ b/avm/res/maintenance/maintenance-configuration/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/managed-identity/user-assigned-identity/README.md b/avm/res/managed-identity/user-assigned-identity/README.md index 06ee4e3ce1..db007ada50 100644 --- a/avm/res/managed-identity/user-assigned-identity/README.md +++ b/avm/res/managed-identity/user-assigned-identity/README.md @@ -121,11 +121,13 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id } roleAssignments: [ { + name: 'b1a2c427-c4b1-435a-9b82-40c1b59537ac' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -194,11 +196,13 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id "roleAssignments": { "value": [ { + "name": "b1a2c427-c4b1-435a-9b82-40c1b59537ac", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -477,6 +481,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -527,6 +532,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/managed-identity/user-assigned-identity/federated-identity-credential/main.json b/avm/res/managed-identity/user-assigned-identity/federated-identity-credential/main.json index d6f9d75fcb..697ad836bc 100644 --- a/avm/res/managed-identity/user-assigned-identity/federated-identity-credential/main.json +++ b/avm/res/managed-identity/user-assigned-identity/federated-identity-credential/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "663270811232806628" + "version": "0.29.47.4906", + "templateHash": "3716898257490923786" }, "name": "User Assigned Identity Federated Identity Credential", "description": "This module deploys a User Assigned Identity Federated Identity Credential.", diff --git a/avm/res/managed-identity/user-assigned-identity/main.bicep b/avm/res/managed-identity/user-assigned-identity/main.bicep index 47e2093109..1a5c7bbccf 100644 --- a/avm/res/managed-identity/user-assigned-identity/main.bicep +++ b/avm/res/managed-identity/user-assigned-identity/main.bicep @@ -45,6 +45,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.managedidentity-userassignedidentity.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -96,14 +107,14 @@ module userAssignedIdentity_federatedIdentityCredentials 'federated-identity-cre ] resource userAssignedIdentity_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(userAssignedIdentity.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + userAssignedIdentity.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -146,6 +157,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/managed-identity/user-assigned-identity/main.json b/avm/res/managed-identity/user-assigned-identity/main.json index 70613c4a9b..1a1e10832f 100644 --- a/avm/res/managed-identity/user-assigned-identity/main.json +++ b/avm/res/managed-identity/user-assigned-identity/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3524074689098361632" + "version": "0.29.47.4906", + "templateHash": "16128849797682252336" }, "name": "User Assigned Identities", "description": "This module deploys a User Assigned Identity.", @@ -43,6 +43,13 @@ "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": { @@ -189,6 +196,13 @@ } }, "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')]", @@ -244,20 +258,20 @@ "userAssignedIdentity_roleAssignments": { "copy": { "name": "userAssignedIdentity_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -301,8 +315,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "663270811232806628" + "version": "0.29.47.4906", + "templateHash": "3716898257490923786" }, "name": "User Assigned Identity Federated Identity Credential", "description": "This module deploys a User Assigned Identity Federated Identity Credential.", diff --git a/avm/res/managed-identity/user-assigned-identity/tests/e2e/max/main.test.bicep b/avm/res/managed-identity/user-assigned-identity/tests/e2e/max/main.test.bicep index 890ed5adff..20a1079dbf 100644 --- a/avm/res/managed-identity/user-assigned-identity/tests/e2e/max/main.test.bicep +++ b/avm/res/managed-identity/user-assigned-identity/tests/e2e/max/main.test.bicep @@ -80,11 +80,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'b1a2c427-c4b1-435a-9b82-40c1b59537ac' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/managed-identity/user-assigned-identity/version.json b/avm/res/managed-identity/user-assigned-identity/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/managed-identity/user-assigned-identity/version.json +++ b/avm/res/managed-identity/user-assigned-identity/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/managed-services/registration-definition/README.md b/avm/res/managed-services/registration-definition/README.md index 51a35a1643..8802eff657 100644 --- a/avm/res/managed-services/registration-definition/README.md +++ b/avm/res/managed-services/registration-definition/README.md @@ -62,7 +62,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: '' name: 'Component Validation - msrdmin Subscription assignment' registrationDescription: 'Managed by Lighthouse' // Non-required parameters @@ -97,7 +97,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d ] }, "managedByTenantId": { - "value": "449fbe1d-9c99-4509-9014-4fd5cf25b014" + "value": "" }, "name": { "value": "Component Validation - msrdmin Subscription assignment" @@ -149,7 +149,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d roleDefinitionId: '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: '' name: 'Component Validation - msrdmax Subscription assignment' registrationDescription: 'Managed by Lighthouse' // Non-required parameters @@ -194,7 +194,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d ] }, "managedByTenantId": { - "value": "449fbe1d-9c99-4509-9014-4fd5cf25b014" + "value": "" }, "name": { "value": "Component Validation - msrdmax Subscription assignment" @@ -240,7 +240,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: '' name: 'Component Validation - msrdrg Subscription assignment' registrationDescription: 'Managed by Lighthouse' // Non-required parameters @@ -277,7 +277,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d ] }, "managedByTenantId": { - "value": "449fbe1d-9c99-4509-9014-4fd5cf25b014" + "value": "" }, "name": { "value": "Component Validation - msrdrg Subscription assignment" @@ -326,7 +326,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: '' name: 'Component Validation - msrdwaf Subscription assignment' registrationDescription: 'Managed by Lighthouse' // Non-required parameters @@ -362,7 +362,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d ] }, "managedByTenantId": { - "value": "449fbe1d-9c99-4509-9014-4fd5cf25b014" + "value": "" }, "name": { "value": "Component Validation - msrdwaf Subscription assignment" diff --git a/avm/res/managed-services/registration-definition/tests/e2e/defaults/main.test.bicep b/avm/res/managed-services/registration-definition/tests/e2e/defaults/main.test.bicep index 0c64dc087e..47ad05d1b6 100644 --- a/avm/res/managed-services/registration-definition/tests/e2e/defaults/main.test.bicep +++ b/avm/res/managed-services/registration-definition/tests/e2e/defaults/main.test.bicep @@ -16,6 +16,10 @@ param resourceLocation string = deployment().location @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.') +@secure() +param lighthouseManagedByTenantId string = '' + // ============== // // Test Execution // // ============== // @@ -38,7 +42,7 @@ module testDeployment '../../../main.bicep' = [ roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: lighthouseManagedByTenantId } } ] diff --git a/avm/res/managed-services/registration-definition/tests/e2e/max/main.test.bicep b/avm/res/managed-services/registration-definition/tests/e2e/max/main.test.bicep index 17fdc6a502..f8f3cb2958 100644 --- a/avm/res/managed-services/registration-definition/tests/e2e/max/main.test.bicep +++ b/avm/res/managed-services/registration-definition/tests/e2e/max/main.test.bicep @@ -16,6 +16,10 @@ param resourceLocation string = deployment().location @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.') +@secure() +param lighthouseManagedByTenantId string = '' + // ============== // // Test Execution // // ============== // @@ -48,7 +52,7 @@ module testDeployment '../../../main.bicep' = [ roleDefinitionId: '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' // User Access Administrator } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: lighthouseManagedByTenantId } } ] diff --git a/avm/res/managed-services/registration-definition/tests/e2e/rg/main.test.bicep b/avm/res/managed-services/registration-definition/tests/e2e/rg/main.test.bicep index 710d6a08ce..979aa9f048 100644 --- a/avm/res/managed-services/registration-definition/tests/e2e/rg/main.test.bicep +++ b/avm/res/managed-services/registration-definition/tests/e2e/rg/main.test.bicep @@ -20,6 +20,10 @@ param resourceLocation string = deployment().location @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.') +@secure() +param lighthouseManagedByTenantId string = '' + // ============ // // Dependencies // // ============ // @@ -61,7 +65,7 @@ module testDeployment '../../../main.bicep' = [ roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: lighthouseManagedByTenantId } } ] diff --git a/avm/res/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep b/avm/res/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep index add8fba41d..9e733740da 100644 --- a/avm/res/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/managed-services/registration-definition/tests/e2e/waf-aligned/main.test.bicep @@ -20,6 +20,10 @@ param resourceLocation string = deployment().location @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.') +@secure() +param lighthouseManagedByTenantId string = '' + // ============ // // Dependencies // // ============ // @@ -54,7 +58,7 @@ module testDeployment '../../../main.bicep' = [ roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46' } ] - managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014' + managedByTenantId: lighthouseManagedByTenantId } } ] diff --git a/avm/res/net-app/net-app-account/README.md b/avm/res/net-app/net-app-account/README.md index 70904aa677..50233d816c 100644 --- a/avm/res/net-app/net-app-account/README.md +++ b/avm/res/net-app/net-app-account/README.md @@ -17,9 +17,13 @@ This module deploys an Azure NetApp File. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.NetApp/netAppAccounts` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts) | -| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts/capacityPools) | -| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts) | +| `Microsoft.NetApp/netAppAccounts/backupPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupPolicies) | +| `Microsoft.NetApp/netAppAccounts/backupVaults` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults) | +| `Microsoft.NetApp/netAppAccounts/backupVaults/backups` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults/backups) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/snapshotPolicies) | ## Usage examples @@ -112,6 +116,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { size: 4398046511104 volumes: [ { + encryptionKeySource: '' exportPolicyRules: [ { allowedClients: '0.0.0.0/0' @@ -123,6 +128,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ] name: 'nanaamax-vol-001' + networkFeatures: 'Standard' protocolTypes: [ 'NFSv4.1' ] @@ -135,8 +141,12 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { ] subnetResourceId: '' usageThreshold: 107374182400 + zones: [ + '1' + ] } { + encryptionKeySource: '' exportPolicyRules: [ { allowedClients: '0.0.0.0/0' @@ -148,11 +158,15 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ] name: 'nanaamax-vol-002' + networkFeatures: 'Standard' protocolTypes: [ 'NFSv4.1' ] subnetResourceId: '' usageThreshold: 107374182400 + zones: [ + '1' + ] } ] } @@ -178,11 +192,13 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } roleAssignments: [ { + name: '18051111-2a33-4f8e-8b24-441aac1e6562' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -238,6 +254,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { "size": 4398046511104, "volumes": [ { + "encryptionKeySource": "", "exportPolicyRules": [ { "allowedClients": "0.0.0.0/0", @@ -249,6 +266,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ], "name": "nanaamax-vol-001", + "networkFeatures": "Standard", "protocolTypes": [ "NFSv4.1" ], @@ -260,9 +278,13 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ], "subnetResourceId": "", - "usageThreshold": 107374182400 + "usageThreshold": 107374182400, + "zones": [ + "1" + ] }, { + "encryptionKeySource": "", "exportPolicyRules": [ { "allowedClients": "0.0.0.0/0", @@ -274,11 +296,15 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ], "name": "nanaamax-vol-002", + "networkFeatures": "Standard", "protocolTypes": [ "NFSv4.1" ], "subnetResourceId": "", - "usageThreshold": 107374182400 + "usageThreshold": 107374182400, + "zones": [ + "1" + ] } ] }, @@ -310,11 +336,13 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { "roleAssignments": { "value": [ { + "name": "18051111-2a33-4f8e-8b24-441aac1e6562", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -374,6 +402,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { size: 4398046511104 volumes: [ { + encryptionKeySource: '' exportPolicyRules: [ { allowedClients: '0.0.0.0/0' @@ -385,6 +414,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ] name: 'nanaanfs3-vol-001' + networkFeatures: 'Standard' protocolTypes: [ 'NFSv3' ] @@ -397,14 +427,22 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { ] subnetResourceId: '' usageThreshold: 107374182400 + zones: [ + '1' + ] } { + encryptionKeySource: '' name: 'nanaanfs3-vol-002' + networkFeatures: 'Standard' protocolTypes: [ 'NFSv3' ] subnetResourceId: '' usageThreshold: 107374182400 + zones: [ + '1' + ] } ] } @@ -489,6 +527,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { "size": 4398046511104, "volumes": [ { + "encryptionKeySource": "", "exportPolicyRules": [ { "allowedClients": "0.0.0.0/0", @@ -500,6 +539,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ], "name": "nanaanfs3-vol-001", + "networkFeatures": "Standard", "protocolTypes": [ "NFSv3" ], @@ -511,15 +551,23 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { } ], "subnetResourceId": "", - "usageThreshold": 107374182400 + "usageThreshold": 107374182400, + "zones": [ + "1" + ] }, { + "encryptionKeySource": "", "name": "nanaanfs3-vol-002", + "networkFeatures": "Standard", "protocolTypes": [ "NFSv3" ], "subnetResourceId": "", - "usageThreshold": 107374182400 + "usageThreshold": 107374182400, + "zones": [ + "1" + ] } ] }, @@ -601,6 +649,9 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { name: 'nanaawaf001' // Non-required parameters location: '' + tags: { + service: 'netapp' + } } } ``` @@ -624,6 +675,11 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { // Non-required parameters "location": { "value": "" + }, + "tags": { + "value": { + "service": "netapp" + } } } } @@ -645,6 +701,7 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { | Parameter | Type | Description | | :-- | :-- | :-- | +| [`adName`](#parameter-adname) | string | Name of the active directory host as part of Kerberos Realm used for Kerberos authentication. | | [`aesEncryption`](#parameter-aesencryption) | bool | Enable AES encryption on the SMB Server. | | [`capacityPools`](#parameter-capacitypools) | array | Capacity pools to create. | | [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | @@ -654,11 +711,15 @@ module netAppAccount 'br/public:avm/res/net-app/net-app-account:' = { | [`domainJoinUser`](#parameter-domainjoinuser) | string | Required if domainName is specified. Username of Active Directory domain administrator, with permissions to create SMB server machine account in the AD domain. | | [`domainName`](#parameter-domainname) | string | Fully Qualified Active Directory DNS Domain Name (e.g. 'contoso.com'). | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`encryptDCConnections`](#parameter-encryptdcconnections) | bool | Specifies whether encryption should be used for communication between SMB server and domain controller (DC). SMB3 only. | +| [`kdcIP`](#parameter-kdcip) | string | Kerberos Key Distribution Center (KDC) as part of Kerberos Realm used for Kerberos authentication. | +| [`ldapOverTLS`](#parameter-ldapovertls) | bool | Specifies whether to use TLS when NFS (with/without Kerberos) and SMB volumes communicate with an LDAP server. A server root CA certificate must be uploaded if enabled (serverRootCACertificate). | | [`ldapSigning`](#parameter-ldapsigning) | bool | Specifies whether or not the LDAP traffic needs to be signed. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`serverRootCACertificate`](#parameter-serverrootcacertificate) | string | A server Root certificate is required of ldapOverTLS is enabled. | | [`smbServerNamePrefix`](#parameter-smbservernameprefix) | string | Required if domainName is specified. NetBIOS name of the SMB server. A computer account with this prefix will be registered in the AD and used to mount volumes. | | [`tags`](#parameter-tags) | object | Tags for all resources. | @@ -669,6 +730,14 @@ The name of the NetApp account. - Required: Yes - Type: string +### Parameter: `adName` + +Name of the active directory host as part of Kerberos Realm used for Kerberos authentication. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `aesEncryption` Enable AES encryption on the SMB Server. @@ -782,6 +851,30 @@ Enable/Disable usage telemetry for module. - Type: bool - Default: `True` +### Parameter: `encryptDCConnections` + +Specifies whether encryption should be used for communication between SMB server and domain controller (DC). SMB3 only. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `kdcIP` + +Kerberos Key Distribution Center (KDC) as part of Kerberos Realm used for Kerberos authentication. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `ldapOverTLS` + +Specifies whether to use TLS when NFS (with/without Kerberos) and SMB volumes communicate with an LDAP server. A server root CA certificate must be uploaded if enabled (serverRootCACertificate). + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `ldapSigning` Specifies whether or not the LDAP traffic needs to be signed. @@ -876,6 +969,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -926,6 +1020,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -943,6 +1044,14 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `serverRootCACertificate` + +A server Root certificate is required of ldapOverTLS is enabled. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `smbServerNamePrefix` Required if domainName is specified. NetBIOS name of the SMB server. A computer account with this prefix will be registered in the AD and used to mount volumes. @@ -967,6 +1076,7 @@ Tags for all resources. | `name` | string | The name of the NetApp account. | | `resourceGroupName` | string | The name of the Resource Group the NetApp account was created in. | | `resourceId` | string | The Resource ID of the NetApp account. | +| `volumeResourceId` | string | The resource IDs of the volume created in the capacity pool. | ## Cross-referenced modules diff --git a/avm/res/net-app/net-app-account/capacity-pool/README.md b/avm/res/net-app/net-app-account/capacity-pool/README.md index d72b6183a5..b0b9dae0d9 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/README.md @@ -15,8 +15,12 @@ This module deploys an Azure NetApp Files Capacity Pool. | Resource Type | API Version | | :-- | :-- | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts/capacityPools) | -| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts/backupPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupPolicies) | +| `Microsoft.NetApp/netAppAccounts/backupVaults` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults) | +| `Microsoft.NetApp/netAppAccounts/backupVaults/backups` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults/backups) | +| `Microsoft.NetApp/netAppAccounts/capacityPools` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/snapshotPolicies) | ## Parameters @@ -25,6 +29,7 @@ This module deploys an Azure NetApp Files Capacity Pool. | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the capacity pool. | +| [`networkFeatures`](#parameter-networkfeatures) | string | Network features available to the volume, or current state of update (Basic/Standard). | | [`size`](#parameter-size) | int | Provisioned size of the pool (in bytes). Allowed values are in 4TiB chunks (value must be multiply of 4398046511104). | **Conditional parameters** @@ -53,6 +58,14 @@ The name of the capacity pool. - Required: Yes - Type: string +### Parameter: `networkFeatures` + +Network features available to the volume, or current state of update (Basic/Standard). + +- Required: No +- Type: string +- Default: `'Standard'` + ### Parameter: `size` Provisioned size of the pool (in bytes). Allowed values are in 4TiB chunks (value must be multiply of 4398046511104). @@ -135,6 +148,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -185,6 +199,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -243,6 +264,7 @@ List of volumnes to create in the capacity pool. | `name` | string | The name of the Capacity Pool. | | `resourceGroupName` | string | The name of the Resource Group the Capacity Pool was created in. | | `resourceId` | string | The resource ID of the Capacity Pool. | +| `volumeResourceId` | string | The resource IDs of the volume created in the capacity pool. | ## Cross-referenced modules diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/main.bicep index 40d3ff8012..60125cc62f 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/main.bicep @@ -23,6 +23,9 @@ param tags object? ]) param serviceLevel string = 'Standard' +@description('Required. Network features available to the volume, or current state of update (Basic/Standard).') +param networkFeatures string = 'Standard' + @description('Required. Provisioned size of the pool (in bytes). Allowed values are in 4TiB chunks (value must be multiply of 4398046511104).') param size int @@ -63,11 +66,22 @@ var builtInRoleNames = { ) } -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2023-11-01' existing = { +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)) + }) +] + +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { name: netAppAccountName } -resource capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2023-11-01' = { +resource capacityPool 'Microsoft.NetApp/netAppAccounts/capacityPools@2024-03-01' = { name: name parent: netAppAccount location: location @@ -91,25 +105,67 @@ module capacityPool_volumes 'volume/main.bicep' = [ name: volume.name location: location serviceLevel: serviceLevel - creationToken: contains(volume, 'creationToken') ? volume.creationToken : volume.name + creationToken: volume.?creationToken ?? volume.name usageThreshold: volume.usageThreshold - protocolTypes: contains(volume, 'protocolTypes') ? volume.protocolTypes : [] + protocolTypes: volume.?protocolTypes ?? [] subnetResourceId: volume.subnetResourceId - exportPolicyRules: contains(volume, 'exportPolicyRules') ? volume.exportPolicyRules : [] - roleAssignments: contains(volume, 'roleAssignments') ? volume.roleAssignments : [] + exportPolicyRules: volume.?exportPolicyRules ?? [] + roleAssignments: volume.?roleAssignments ?? [] + networkFeatures: volume.?networkFeatures + zones: volume.?zones + coolAccess: volume.?coolAccess ?? false + coolAccessRetrievalPolicy: volume.?coolAccessRetrievalPolicy ?? 'Default' + coolnessPeriod: volume.?coolnessPeriod ?? 0 + encryptionKeySource: volume.?encryptionKeySource ?? 'Microsoft.NetApp' + keyVaultPrivateEndpointResourceId: volume.?keyVaultPrivateEndpointResourceId ?? '' + endpointType: volume.?endpointType ?? '' + remoteVolumeRegion: volume.?remoteVolumeRegion ?? '' + remoteVolumeResourceId: volume.?remoteVolumeResourceId ?? '' + replicationSchedule: volume.?replicationSchedule ?? '' + snapshotPolicyId: volume.?snapshotPolicyId ?? '' + snapshotPolicyName: volume.?snapshotPolicyName ?? 'snapshotPolicy' + snapshotPolicyLocation: volume.?snapshotPolicyLocation ?? '' + snapEnabled: volume.?snapEnabled ?? false + dailyHour: volume.?dailyHour ?? 0 + dailyMinute: volume.?dailyMinute ?? 0 + dailySnapshotsToKeep: volume.?dailySnapshotsToKeep ?? 0 + dailyUsedBytes: volume.?dailyUsedBytes ?? 0 + hourlyMinute: volume.?hourlyMinute ?? 0 + hourlySnapshotsToKeep: volume.?hourlySnapshotsToKeep ?? 0 + hourlyUsedBytes: volume.?hourlyUsedBytes ?? 0 + daysOfMonth: volume.?daysOfMonth ?? '' + monthlyHour: volume.?monthlyHour ?? 0 + monthlyMinute: volume.?monthlyMinute ?? 0 + monthlySnapshotsToKeep: volume.?monthlySnapshotsToKeep ?? 0 + monthlyUsedBytes: volume.?monthlyUsedBytes ?? 0 + weeklyDay: volume.?weeklyDay ?? '' + weeklyHour: volume.?weeklyHour ?? 0 + weeklyMinute: volume.?weeklyMinute ?? 0 + weeklySnapshotsToKeep: volume.?weeklySnapshotsToKeep ?? 0 + weeklyUsedBytes: volume.?weeklyUsedBytes ?? 0 + backupPolicyName: volume.?backupPolicyName ?? 'backupPolicy' + backupPolicyLocation: volume.?backupPolicyLocation ?? '' + dailyBackupsToKeep: volume.?dailyBackupsToKeep ?? 0 + backupEnabled: volume.?backupEnabled ?? false + monthlyBackupsToKeep: volume.?monthlyBackupsToKeep ?? 0 + weeklyBackupsToKeep: volume.?weeklyBackupsToKeep ?? 0 + backupVaultName: volume.?backupVaultName ?? 'vault' + backupVaultLocation: volume.?backupVaultLocation ?? '' + backupName: volume.?backupName ?? 'backup' + backupLabel: volume.?backupLabel ?? '' + snapshotName: volume.?snapshotName ?? 'snapshot' + useExistingSnapshot: volume.?useExistingSnapshot ?? false + volumeResourceId: volume.?volumeResourceId ?? '' + volumeType: volume.?volumeType ?? '' } } ] resource capacityPool_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(capacityPool.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(capacityPool.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -133,11 +189,17 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = capacityPool.location +@description('The resource IDs of the volume created in the capacity pool.') +output volumeResourceId string = (volumes != []) ? capacityPool_volumes[0].outputs.resourceId : '' + // =============== // // Definitions // // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.json b/avm/res/net-app/net-app-account/capacity-pool/main.json index 4c05f6cc2a..b8ed42b918 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13254524547530770866" + "version": "0.29.47.4906", + "templateHash": "2991493712029822030" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool.", @@ -18,6 +18,13 @@ "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": { @@ -120,6 +127,13 @@ "description": "Optional. The pool service level." } }, + "networkFeatures": { + "type": "string", + "defaultValue": "Standard", + "metadata": { + "description": "Required. Network features available to the volume, or current state of update (Basic/Standard)." + } + }, "size": { "type": "int", "metadata": { @@ -170,6 +184,13 @@ } }, "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')]", @@ -182,12 +203,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('netAppAccountName')]" }, "capacityPool": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -205,20 +226,20 @@ "capacityPool_roleAssignments": { "copy": { "name": "capacityPool_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}/capacityPools/{1}', parameters('netAppAccountName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools', parameters('netAppAccountName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools', parameters('netAppAccountName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "capacityPool" @@ -255,16 +276,162 @@ "serviceLevel": { "value": "[parameters('serviceLevel')]" }, - "creationToken": "[if(contains(parameters('volumes')[copyIndex()], 'creationToken'), createObject('value', parameters('volumes')[copyIndex()].creationToken), createObject('value', parameters('volumes')[copyIndex()].name))]", + "creationToken": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'creationToken'), parameters('volumes')[copyIndex()].name)]" + }, "usageThreshold": { "value": "[parameters('volumes')[copyIndex()].usageThreshold]" }, - "protocolTypes": "[if(contains(parameters('volumes')[copyIndex()], 'protocolTypes'), createObject('value', parameters('volumes')[copyIndex()].protocolTypes), createObject('value', createArray()))]", + "protocolTypes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'protocolTypes'), createArray())]" + }, "subnetResourceId": { "value": "[parameters('volumes')[copyIndex()].subnetResourceId]" }, - "exportPolicyRules": "[if(contains(parameters('volumes')[copyIndex()], 'exportPolicyRules'), createObject('value', parameters('volumes')[copyIndex()].exportPolicyRules), createObject('value', createArray()))]", - "roleAssignments": "[if(contains(parameters('volumes')[copyIndex()], 'roleAssignments'), createObject('value', parameters('volumes')[copyIndex()].roleAssignments), createObject('value', createArray()))]" + "exportPolicyRules": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'exportPolicyRules'), createArray())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'roleAssignments'), createArray())]" + }, + "networkFeatures": { + "value": "[tryGet(parameters('volumes')[copyIndex()], 'networkFeatures')]" + }, + "zones": { + "value": "[tryGet(parameters('volumes')[copyIndex()], 'zones')]" + }, + "coolAccess": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolAccess'), false())]" + }, + "coolAccessRetrievalPolicy": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolAccessRetrievalPolicy'), 'Default')]" + }, + "coolnessPeriod": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolnessPeriod'), 0)]" + }, + "encryptionKeySource": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'encryptionKeySource'), 'Microsoft.NetApp')]" + }, + "keyVaultPrivateEndpointResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'keyVaultPrivateEndpointResourceId'), '')]" + }, + "endpointType": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'endpointType'), '')]" + }, + "remoteVolumeRegion": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'remoteVolumeRegion'), '')]" + }, + "remoteVolumeResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'remoteVolumeResourceId'), '')]" + }, + "replicationSchedule": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'replicationSchedule'), '')]" + }, + "snapshotPolicyId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyId'), '')]" + }, + "snapshotPolicyName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyName'), 'snapshotPolicy')]" + }, + "snapshotPolicyLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyLocation'), '')]" + }, + "snapEnabled": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapEnabled'), false())]" + }, + "dailyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyHour'), 0)]" + }, + "dailyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyMinute'), 0)]" + }, + "dailySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailySnapshotsToKeep'), 0)]" + }, + "dailyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyUsedBytes'), 0)]" + }, + "hourlyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlyMinute'), 0)]" + }, + "hourlySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlySnapshotsToKeep'), 0)]" + }, + "hourlyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlyUsedBytes'), 0)]" + }, + "daysOfMonth": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'daysOfMonth'), '')]" + }, + "monthlyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyHour'), 0)]" + }, + "monthlyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyMinute'), 0)]" + }, + "monthlySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlySnapshotsToKeep'), 0)]" + }, + "monthlyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyUsedBytes'), 0)]" + }, + "weeklyDay": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyDay'), '')]" + }, + "weeklyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyHour'), 0)]" + }, + "weeklyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyMinute'), 0)]" + }, + "weeklySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklySnapshotsToKeep'), 0)]" + }, + "weeklyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyUsedBytes'), 0)]" + }, + "backupPolicyName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupPolicyName'), 'backupPolicy')]" + }, + "backupPolicyLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupPolicyLocation'), '')]" + }, + "dailyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyBackupsToKeep'), 0)]" + }, + "backupEnabled": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupEnabled'), false())]" + }, + "monthlyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyBackupsToKeep'), 0)]" + }, + "weeklyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyBackupsToKeep'), 0)]" + }, + "backupVaultName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupVaultName'), 'vault')]" + }, + "backupVaultLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupVaultLocation'), '')]" + }, + "backupName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupName'), 'backup')]" + }, + "backupLabel": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupLabel'), '')]" + }, + "snapshotName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotName'), 'snapshot')]" + }, + "useExistingSnapshot": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'useExistingSnapshot'), false())]" + }, + "volumeResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'volumeResourceId'), '')]" + }, + "volumeType": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'volumeType'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -273,8 +440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "308407733537316660" + "version": "0.29.47.4906", + "templateHash": "6870432140728658293" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -286,6 +453,13 @@ "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": { @@ -361,6 +535,278 @@ "description": "Conditional. The name of the parent capacity pool. Required if the template is used in a standalone deployment." } }, + "coolAccess": { + "type": "bool", + "metadata": { + "description": "Optional. If enabled (true) the pool can contain cool Access enabled volumes." + } + }, + "coolnessPeriod": { + "type": "int", + "metadata": { + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." + } + }, + "coolAccessRetrievalPolicy": { + "type": "string", + "defaultValue": "Default", + "metadata": { + "description": "Optional. determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." + } + }, + "encryptionKeySource": { + "type": "string", + "metadata": { + "description": "Optional. The source of the encryption key." + } + }, + "keyVaultPrivateEndpointResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the key vault private endpoint." + } + }, + "endpointType": { + "type": "string", + "metadata": { + "description": "Optional. Indicates whether the local volume is the source or destination for the Volume Replication (src/dst)." + } + }, + "remoteVolumeRegion": { + "type": "string", + "metadata": { + "description": "Optional. The remote region for the other end of the Volume Replication." + } + }, + "remoteVolumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the remote volume." + } + }, + "replicationSchedule": { + "type": "string", + "metadata": { + "description": "Optional. The replication schedule for the volume." + } + }, + "backupEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the backup policy is enabled." + } + }, + "backupPolicyName": { + "type": "string", + "defaultValue": "backupPolicy", + "metadata": { + "description": "Optional. The name of the backup policy." + } + }, + "backupPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup policy." + } + }, + "dailyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The daily backups to keep." + } + }, + "monthlyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The monthly backups to keep." + } + }, + "weeklyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The weekly backups to keep." + } + }, + "backupVaultName": { + "type": "string", + "defaultValue": "vault", + "metadata": { + "description": "Optional. The name of the backup vault." + } + }, + "backupVaultLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup vault." + } + }, + "backupName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the backup." + } + }, + "backupLabel": { + "type": "string", + "metadata": { + "description": "Optional. The label of the backup." + } + }, + "useExistingSnapshot": { + "type": "bool", + "metadata": { + "description": "Optional. Indicates whether to use an existing snapshot." + } + }, + "snapshotName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot." + } + }, + "snapshotPolicyId": { + "type": "string", + "metadata": { + "description": "Optional. Snapshot Policy ResourceId." + } + }, + "snapshotPolicyName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot policy." + } + }, + "snapshotPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the snapshot policy." + } + }, + "dailyHour": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot hour." + } + }, + "dailyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot minute." + } + }, + "dailySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot count to keep." + } + }, + "dailyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot used bytes." + } + }, + "hourlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The hourly snapshot minute." + } + }, + "hourlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot count to keep." + } + }, + "hourlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot used bytes." + } + }, + "daysOfMonth": { + "type": "string", + "metadata": { + "description": "Optional. The monthly snapshot day." + } + }, + "monthlyHour": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot hour." + } + }, + "monthlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot minute." + } + }, + "monthlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot count to keep." + } + }, + "monthlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot used bytes." + } + }, + "weeklyDay": { + "type": "string", + "metadata": { + "description": "Optional. The weekly snapshot day." + } + }, + "weeklyHour": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot hour." + } + }, + "weeklyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot minute." + } + }, + "weeklySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot count to keep." + } + }, + "weeklyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot used bytes." + } + }, + "snapEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the snapshot policy is enabled." + } + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the volume." + } + }, + "volumeType": { + "type": "string", + "metadata": { + "description": "Optional. The type of the volume. DataProtection volumes are used for replication." + } + }, "name": { "type": "string", "metadata": { @@ -374,6 +820,15 @@ "description": "Optional. Location of the pool volume." } }, + "zones": { + "type": "array", + "defaultValue": [ + "1" + ], + "metadata": { + "description": "Optional. Zone where the volume will be placed." + } + }, "serviceLevel": { "type": "string", "defaultValue": "Standard", @@ -387,6 +842,19 @@ "description": "Optional. The pool service level. Must match the one of the parent capacity pool." } }, + "networkFeatures": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Basic_Standard", + "Standard", + "Standard_Basic" + ], + "metadata": { + "description": "Optional. Network feature for the volume." + } + }, "creationToken": { "type": "string", "defaultValue": "[parameters('name')]", @@ -428,6 +896,13 @@ } }, "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')]", @@ -440,7 +915,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]", "dependsOn": [ "netAppAccount" @@ -449,43 +924,112 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('netAppAccountName')]" }, "volume": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', parameters('keyVaultPrivateEndpointResourceId')), createObject()), if(not(equals(parameters('volumeType'), '')), createObject('volumeType', parameters('volumeType'), 'dataProtection', createObject('replication', createObject('endpointType', parameters('endpointType'), 'remoteVolumeRegion', parameters('remoteVolumeRegion'), 'remoteVolumeResourceId', parameters('remoteVolumeResourceId'), 'replicationSchedule', parameters('replicationSchedule')), 'snapshot', createObject('snapshotPolicyId', parameters('snapshotPolicyId')))), createObject()), createObject('networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', parameters('subnetResourceId'), 'exportPolicy', if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null()))))]", + "zones": "[parameters('zones')]", + "dependsOn": [ + "netAppAccount::capacityPool" + ] + }, + "snapshotPolicies": { + "condition": "[parameters('snapEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('snapshotPolicyName'))]", + "location": "[parameters('snapshotPolicyLocation')]", "properties": { - "serviceLevel": "[parameters('serviceLevel')]", - "creationToken": "[parameters('creationToken')]", - "usageThreshold": "[parameters('usageThreshold')]", - "protocolTypes": "[parameters('protocolTypes')]", - "subnetId": "[parameters('subnetResourceId')]", - "exportPolicy": "[if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null())]" + "enabled": "[parameters('snapEnabled')]", + "dailySchedule": { + "hour": "[parameters('dailyHour')]", + "minute": "[parameters('dailyMinute')]", + "snapshotsToKeep": "[parameters('dailySnapshotsToKeep')]", + "usedBytes": "[parameters('dailyUsedBytes')]" + }, + "hourlySchedule": { + "minute": "[parameters('hourlyMinute')]", + "snapshotsToKeep": "[parameters('hourlySnapshotsToKeep')]", + "usedBytes": "[parameters('hourlyUsedBytes')]" + }, + "monthlySchedule": { + "daysOfMonth": "[parameters('daysOfMonth')]", + "hour": "[parameters('monthlyHour')]", + "minute": "[parameters('monthlyMinute')]", + "snapshotsToKeep": "[parameters('monthlySnapshotsToKeep')]", + "usedBytes": "[parameters('monthlyUsedBytes')]" + }, + "weeklySchedule": { + "day": "[parameters('weeklyDay')]", + "hour": "[parameters('weeklyHour')]", + "minute": "[parameters('weeklyMinute')]", + "snapshotsToKeep": "[parameters('weeklySnapshotsToKeep')]", + "usedBytes": "[parameters('weeklyUsedBytes')]" + } }, "dependsOn": [ - "netAppAccount::capacityPool" + "netAppAccount" + ] + }, + "backupPolicies": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupPolicyName'))]", + "location": "[parameters('backupPolicyLocation')]", + "properties": { + "dailyBackupsToKeep": "[parameters('dailyBackupsToKeep')]", + "enabled": "[parameters('backupEnabled')]", + "monthlyBackupsToKeep": "[parameters('monthlyBackupsToKeep')]", + "weeklyBackupsToKeep": "[parameters('weeklyBackupsToKeep')]" + }, + "dependsOn": [ + "netAppAccount" + ] + }, + "backupVaults": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupVaultName'))]", + "location": "[parameters('backupVaultLocation')]", + "properties": {}, + "dependsOn": [ + "netAppAccount" + ] + }, + "backups": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults/backups", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('backupVaultName'), parameters('backupName'))]", + "properties": "[if(parameters('backupEnabled'), createObject('label', parameters('backupLabel'), 'snapshotName', parameters('snapshotName'), 'useExistingSnapshot', parameters('useExistingSnapshot'), 'volumeResourceId', parameters('volumeResourceId')), createObject())]", + "dependsOn": [ + "backupVaults" ] }, "volume_roleAssignments": { "copy": { "name": "volume_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}/capacityPools/{1}/volumes/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "volume" @@ -519,7 +1063,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('volume', '2023-11-01', 'full').location]" + "value": "[reference('volume', '2024-03-01', 'full').location]" } } } @@ -557,7 +1101,14 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('capacityPool', '2023-11-01', 'full').location]" + "value": "[reference('capacityPool', '2024-03-01', 'full').location]" + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "The resource IDs of the volume created in the capacity pool." + }, + "value": "[if(not(equals(parameters('volumes'), createArray())), reference(format('capacityPool_volumes[{0}]', 0)).outputs.resourceId.value, '')]" } } } \ No newline at end of file diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md index e2e33a659a..f5bd8baafa 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md @@ -15,7 +15,11 @@ This module deploys an Azure NetApp Files Capacity Pool Volume. | Resource Type | API Version | | :-- | :-- | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts/backupPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupPolicies) | +| `Microsoft.NetApp/netAppAccounts/backupVaults` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults) | +| `Microsoft.NetApp/netAppAccounts/backupVaults/backups` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/backupVaults/backups) | +| `Microsoft.NetApp/netAppAccounts/capacityPools/volumes` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/capacityPools/volumes) | +| `Microsoft.NetApp/netAppAccounts/snapshotPolicies` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.NetApp/2024-03-01/netAppAccounts/snapshotPolicies) | ## Parameters @@ -38,12 +42,58 @@ This module deploys an Azure NetApp Files Capacity Pool Volume. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`backupEnabled`](#parameter-backupenabled) | bool | Indicates whether the backup policy is enabled. | +| [`backupLabel`](#parameter-backuplabel) | string | The label of the backup. | +| [`backupName`](#parameter-backupname) | string | The name of the backup. | +| [`backupPolicyLocation`](#parameter-backuppolicylocation) | string | The location of the backup policy. | +| [`backupPolicyName`](#parameter-backuppolicyname) | string | The name of the backup policy. | +| [`backupVaultLocation`](#parameter-backupvaultlocation) | string | The location of the backup vault. | +| [`backupVaultName`](#parameter-backupvaultname) | string | The name of the backup vault. | +| [`coolAccess`](#parameter-coolaccess) | bool | If enabled (true) the pool can contain cool Access enabled volumes. | +| [`coolAccessRetrievalPolicy`](#parameter-coolaccessretrievalpolicy) | string | determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read). | +| [`coolnessPeriod`](#parameter-coolnessperiod) | int | Specifies the number of days after which data that is not accessed by clients will be tiered. | | [`creationToken`](#parameter-creationtoken) | string | A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription. | +| [`dailyBackupsToKeep`](#parameter-dailybackupstokeep) | int | The daily backups to keep. | +| [`dailyHour`](#parameter-dailyhour) | int | The daily snapshot hour. | +| [`dailyMinute`](#parameter-dailyminute) | int | The daily snapshot minute. | +| [`dailySnapshotsToKeep`](#parameter-dailysnapshotstokeep) | int | Daily snapshot count to keep. | +| [`dailyUsedBytes`](#parameter-dailyusedbytes) | int | Daily snapshot used bytes. | +| [`daysOfMonth`](#parameter-daysofmonth) | string | The monthly snapshot day. | +| [`encryptionKeySource`](#parameter-encryptionkeysource) | string | The source of the encryption key. | +| [`endpointType`](#parameter-endpointtype) | string | Indicates whether the local volume is the source or destination for the Volume Replication (src/dst). | | [`exportPolicyRules`](#parameter-exportpolicyrules) | array | Export policy rules. | +| [`hourlyMinute`](#parameter-hourlyminute) | int | The hourly snapshot minute. | +| [`hourlySnapshotsToKeep`](#parameter-hourlysnapshotstokeep) | int | Hourly snapshot count to keep. | +| [`hourlyUsedBytes`](#parameter-hourlyusedbytes) | int | Hourly snapshot used bytes. | +| [`keyVaultPrivateEndpointResourceId`](#parameter-keyvaultprivateendpointresourceid) | string | The resource ID of the key vault private endpoint. | | [`location`](#parameter-location) | string | Location of the pool volume. | +| [`monthlyBackupsToKeep`](#parameter-monthlybackupstokeep) | int | The monthly backups to keep. | +| [`monthlyHour`](#parameter-monthlyhour) | int | The monthly snapshot hour. | +| [`monthlyMinute`](#parameter-monthlyminute) | int | The monthly snapshot minute. | +| [`monthlySnapshotsToKeep`](#parameter-monthlysnapshotstokeep) | int | Monthly snapshot count to keep. | +| [`monthlyUsedBytes`](#parameter-monthlyusedbytes) | int | Monthly snapshot used bytes. | +| [`networkFeatures`](#parameter-networkfeatures) | string | Network feature for the volume. | | [`protocolTypes`](#parameter-protocoltypes) | array | Set of protocol types. | +| [`remoteVolumeRegion`](#parameter-remotevolumeregion) | string | The remote region for the other end of the Volume Replication. | +| [`remoteVolumeResourceId`](#parameter-remotevolumeresourceid) | string | The resource ID of the remote volume. | +| [`replicationSchedule`](#parameter-replicationschedule) | string | The replication schedule for the volume. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`serviceLevel`](#parameter-servicelevel) | string | The pool service level. Must match the one of the parent capacity pool. | +| [`snapEnabled`](#parameter-snapenabled) | bool | Indicates whether the snapshot policy is enabled. | +| [`snapshotName`](#parameter-snapshotname) | string | The name of the snapshot. | +| [`snapshotPolicyId`](#parameter-snapshotpolicyid) | string | Snapshot Policy ResourceId. | +| [`snapshotPolicyLocation`](#parameter-snapshotpolicylocation) | string | The location of the snapshot policy. | +| [`snapshotPolicyName`](#parameter-snapshotpolicyname) | string | The name of the snapshot policy. | +| [`useExistingSnapshot`](#parameter-useexistingsnapshot) | bool | Indicates whether to use an existing snapshot. | +| [`volumeResourceId`](#parameter-volumeresourceid) | string | The resource ID of the volume. | +| [`volumeType`](#parameter-volumetype) | string | The type of the volume. DataProtection volumes are used for replication. | +| [`weeklyBackupsToKeep`](#parameter-weeklybackupstokeep) | int | The weekly backups to keep. | +| [`weeklyDay`](#parameter-weeklyday) | string | The weekly snapshot day. | +| [`weeklyHour`](#parameter-weeklyhour) | int | The weekly snapshot hour. | +| [`weeklyMinute`](#parameter-weeklyminute) | int | The weekly snapshot minute. | +| [`weeklySnapshotsToKeep`](#parameter-weeklysnapshotstokeep) | int | Weekly snapshot count to keep. | +| [`weeklyUsedBytes`](#parameter-weeklyusedbytes) | int | Weekly snapshot used bytes. | +| [`zones`](#parameter-zones) | array | Zone where the volume will be placed. | ### Parameter: `name` @@ -80,6 +130,82 @@ The name of the parent NetApp account. Required if the template is used in a sta - Required: Yes - Type: string +### Parameter: `backupEnabled` + +Indicates whether the backup policy is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `backupLabel` + +The label of the backup. + +- Required: Yes +- Type: string + +### Parameter: `backupName` + +The name of the backup. + +- Required: Yes +- Type: string + +### Parameter: `backupPolicyLocation` + +The location of the backup policy. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `backupPolicyName` + +The name of the backup policy. + +- Required: No +- Type: string +- Default: `'backupPolicy'` + +### Parameter: `backupVaultLocation` + +The location of the backup vault. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `backupVaultName` + +The name of the backup vault. + +- Required: No +- Type: string +- Default: `'vault'` + +### Parameter: `coolAccess` + +If enabled (true) the pool can contain cool Access enabled volumes. + +- Required: Yes +- Type: bool + +### Parameter: `coolAccessRetrievalPolicy` + +determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read). + +- Required: No +- Type: string +- Default: `'Default'` + +### Parameter: `coolnessPeriod` + +Specifies the number of days after which data that is not accessed by clients will be tiered. + +- Required: Yes +- Type: int + ### Parameter: `creationToken` A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription. @@ -88,6 +214,62 @@ A unique file path for the volume. This is the name of the volume export. A volu - Type: string - Default: `[parameters('name')]` +### Parameter: `dailyBackupsToKeep` + +The daily backups to keep. + +- Required: Yes +- Type: int + +### Parameter: `dailyHour` + +The daily snapshot hour. + +- Required: Yes +- Type: int + +### Parameter: `dailyMinute` + +The daily snapshot minute. + +- Required: Yes +- Type: int + +### Parameter: `dailySnapshotsToKeep` + +Daily snapshot count to keep. + +- Required: Yes +- Type: int + +### Parameter: `dailyUsedBytes` + +Daily snapshot used bytes. + +- Required: Yes +- Type: int + +### Parameter: `daysOfMonth` + +The monthly snapshot day. + +- Required: Yes +- Type: string + +### Parameter: `encryptionKeySource` + +The source of the encryption key. + +- Required: Yes +- Type: string + +### Parameter: `endpointType` + +Indicates whether the local volume is the source or destination for the Volume Replication (src/dst). + +- Required: Yes +- Type: string + ### Parameter: `exportPolicyRules` Export policy rules. @@ -96,6 +278,34 @@ Export policy rules. - Type: array - Default: `[]` +### Parameter: `hourlyMinute` + +The hourly snapshot minute. + +- Required: Yes +- Type: int + +### Parameter: `hourlySnapshotsToKeep` + +Hourly snapshot count to keep. + +- Required: Yes +- Type: int + +### Parameter: `hourlyUsedBytes` + +Hourly snapshot used bytes. + +- Required: Yes +- Type: int + +### Parameter: `keyVaultPrivateEndpointResourceId` + +The resource ID of the key vault private endpoint. + +- Required: Yes +- Type: string + ### Parameter: `location` Location of the pool volume. @@ -104,6 +314,58 @@ Location of the pool volume. - Type: string - Default: `[resourceGroup().location]` +### Parameter: `monthlyBackupsToKeep` + +The monthly backups to keep. + +- Required: Yes +- Type: int + +### Parameter: `monthlyHour` + +The monthly snapshot hour. + +- Required: Yes +- Type: int + +### Parameter: `monthlyMinute` + +The monthly snapshot minute. + +- Required: Yes +- Type: int + +### Parameter: `monthlySnapshotsToKeep` + +Monthly snapshot count to keep. + +- Required: Yes +- Type: int + +### Parameter: `monthlyUsedBytes` + +Monthly snapshot used bytes. + +- Required: Yes +- Type: int + +### Parameter: `networkFeatures` + +Network feature for the volume. + +- Required: No +- Type: string +- Default: `'Standard'` +- Allowed: + ```Bicep + [ + 'Basic' + 'Basic_Standard' + 'Standard' + 'Standard_Basic' + ] + ``` + ### Parameter: `protocolTypes` Set of protocol types. @@ -112,6 +374,27 @@ Set of protocol types. - Type: array - Default: `[]` +### Parameter: `remoteVolumeRegion` + +The remote region for the other end of the Volume Replication. + +- Required: Yes +- Type: string + +### Parameter: `remoteVolumeResourceId` + +The resource ID of the remote volume. + +- Required: Yes +- Type: string + +### Parameter: `replicationSchedule` + +The replication schedule for the volume. + +- Required: Yes +- Type: string + ### Parameter: `roleAssignments` Array of role assignments to create. @@ -134,6 +417,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -184,6 +468,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -218,6 +509,119 @@ The pool service level. Must match the one of the parent capacity pool. ] ``` +### Parameter: `snapEnabled` + +Indicates whether the snapshot policy is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `snapshotName` + +The name of the snapshot. + +- Required: Yes +- Type: string + +### Parameter: `snapshotPolicyId` + +Snapshot Policy ResourceId. + +- Required: Yes +- Type: string + +### Parameter: `snapshotPolicyLocation` + +The location of the snapshot policy. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `snapshotPolicyName` + +The name of the snapshot policy. + +- Required: Yes +- Type: string + +### Parameter: `useExistingSnapshot` + +Indicates whether to use an existing snapshot. + +- Required: Yes +- Type: bool + +### Parameter: `volumeResourceId` + +The resource ID of the volume. + +- Required: Yes +- Type: string + +### Parameter: `volumeType` + +The type of the volume. DataProtection volumes are used for replication. + +- Required: Yes +- Type: string + +### Parameter: `weeklyBackupsToKeep` + +The weekly backups to keep. + +- Required: Yes +- Type: int + +### Parameter: `weeklyDay` + +The weekly snapshot day. + +- Required: Yes +- Type: string + +### Parameter: `weeklyHour` + +The weekly snapshot hour. + +- Required: Yes +- Type: int + +### Parameter: `weeklyMinute` + +The weekly snapshot minute. + +- Required: Yes +- Type: int + +### Parameter: `weeklySnapshotsToKeep` + +Weekly snapshot count to keep. + +- Required: Yes +- Type: int + +### Parameter: `weeklyUsedBytes` + +Weekly snapshot used bytes. + +- Required: Yes +- Type: int + +### Parameter: `zones` + +Zone where the volume will be placed. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + '1' + ] + ``` + ## Outputs diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep index 1ec28707cb..b9b26d20bf 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep @@ -8,12 +8,147 @@ param netAppAccountName string @description('Conditional. The name of the parent capacity pool. Required if the template is used in a standalone deployment.') param capacityPoolName string +@description('Optional. If enabled (true) the pool can contain cool Access enabled volumes.') +param coolAccess bool + +@description('Optional. Specifies the number of days after which data that is not accessed by clients will be tiered.') +param coolnessPeriod int + +@description('Optional. determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read).') +param coolAccessRetrievalPolicy string = 'Default' + +@description('Optional. The source of the encryption key.') +param encryptionKeySource string + +@description('Optional. The resource ID of the key vault private endpoint.') +param keyVaultPrivateEndpointResourceId string + +@description('Optional. Indicates whether the local volume is the source or destination for the Volume Replication (src/dst).') +param endpointType string + +@description('Optional. The remote region for the other end of the Volume Replication.') +param remoteVolumeRegion string + +@description('Optional. The resource ID of the remote volume.') +param remoteVolumeResourceId string + +@description('Optional. The replication schedule for the volume.') +param replicationSchedule string + +@description('Optional. Indicates whether the backup policy is enabled.') +param backupEnabled bool = false + +@description('Optional. The name of the backup policy.') +param backupPolicyName string = 'backupPolicy' + +@description('Optional. The location of the backup policy.') +param backupPolicyLocation string = resourceGroup().location + +@description('Optional. The daily backups to keep.') +param dailyBackupsToKeep int + +@description('Optional. The monthly backups to keep.') +param monthlyBackupsToKeep int + +@description('Optional. The weekly backups to keep.') +param weeklyBackupsToKeep int + +@description('Optional. The name of the backup vault.') +param backupVaultName string = 'vault' + +@description('Optional. The location of the backup vault.') +param backupVaultLocation string = resourceGroup().location + +@description('Optional. The name of the backup.') +param backupName string + +@description('Optional. The label of the backup.') +param backupLabel string + +@description('Optional. Indicates whether to use an existing snapshot.') +param useExistingSnapshot bool + +@description('Optional. The name of the snapshot.') +param snapshotName string + +@description('Optional. Snapshot Policy ResourceId.') +param snapshotPolicyId string + +@description('Optional. The name of the snapshot policy.') +param snapshotPolicyName string + +@description('Optional. The location of the snapshot policy.') +param snapshotPolicyLocation string = resourceGroup().location + +@description('Optional. The daily snapshot hour.') +param dailyHour int + +@description('Optional. The daily snapshot minute.') +param dailyMinute int + +@description('Optional. Daily snapshot count to keep.') +param dailySnapshotsToKeep int + +@description('Optional. Daily snapshot used bytes.') +param dailyUsedBytes int + +@description('Optional. The hourly snapshot minute.') +param hourlyMinute int + +@description('Optional. Hourly snapshot count to keep.') +param hourlySnapshotsToKeep int + +@description('Optional. Hourly snapshot used bytes.') +param hourlyUsedBytes int + +@description('Optional. The monthly snapshot day.') +param daysOfMonth string + +@description('Optional. The monthly snapshot hour.') +param monthlyHour int + +@description('Optional. The monthly snapshot minute.') +param monthlyMinute int + +@description('Optional. Monthly snapshot count to keep.') +param monthlySnapshotsToKeep int + +@description('Optional. Monthly snapshot used bytes.') +param monthlyUsedBytes int + +@description('Optional. The weekly snapshot day.') +param weeklyDay string + +@description('Optional. The weekly snapshot hour.') +param weeklyHour int + +@description('Optional. The weekly snapshot minute.') +param weeklyMinute int + +@description('Optional. Weekly snapshot count to keep.') +param weeklySnapshotsToKeep int + +@description('Optional. Weekly snapshot used bytes.') +param weeklyUsedBytes int + +@description('Optional. Indicates whether the snapshot policy is enabled.') +param snapEnabled bool = false + +@description('Optional. The resource ID of the volume.') +param volumeResourceId string + +@description('Optional. The type of the volume. DataProtection volumes are used for replication.') +param volumeType string + @description('Required. The name of the pool volume.') param name string @description('Optional. Location of the pool volume.') param location string = resourceGroup().location +@description('Optional. Zone where the volume will be placed.') +param zones array = ['1'] + @description('Optional. The pool service level. Must match the one of the parent capacity pool.') @allowed([ 'Premium' @@ -23,6 +158,15 @@ param location string = resourceGroup().location ]) param serviceLevel string = 'Standard' +@description('Optional. Network feature for the volume.') +@allowed([ + 'Basic' + 'Basic_Standard' + 'Standard' + 'Standard_Basic' +]) +param networkFeatures string = 'Standard' + @description('Optional. A unique file path for the volume. This is the name of the volume export. A volume is mounted using the export path. File path must start with an alphabetical character and be unique within the subscription.') param creationToken string = name @@ -55,19 +199,56 @@ var builtInRoleNames = { ) } -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2023-11-01' existing = { +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)) + }) +] + +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' existing = { name: netAppAccountName - resource capacityPool 'capacityPools@2023-11-01' existing = { + resource capacityPool 'capacityPools@2024-03-01' existing = { name: capacityPoolName } } -resource volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2023-11-01' = { +resource volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2024-03-01' = { name: name parent: netAppAccount::capacityPool location: location properties: { + coolAccess: coolAccess + coolAccessRetrievalPolicy: coolAccessRetrievalPolicy + coolnessPeriod: coolnessPeriod + encryptionKeySource: encryptionKeySource + ...(encryptionKeySource != 'Microsoft.NetApp' + ? { + keyVaultPrivateEndpointResourceId: keyVaultPrivateEndpointResourceId + } + : {}) + ...(volumeType != '' + ? { + volumeType: volumeType + dataProtection: { + replication: { + endpointType: endpointType + remoteVolumeRegion: remoteVolumeRegion + remoteVolumeResourceId: remoteVolumeResourceId + replicationSchedule: replicationSchedule + } + snapshot: { + snapshotPolicyId: snapshotPolicyId + } + } + } + : {}) + networkFeatures: networkFeatures serviceLevel: serviceLevel creationToken: creationToken usageThreshold: usageThreshold @@ -79,17 +260,80 @@ resource volume 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes@2023-11-0 } : null } + zones: zones +} + +resource snapshotPolicies 'Microsoft.NetApp/netAppAccounts/snapshotPolicies@2024-03-01' = if (snapEnabled) { + name: snapshotPolicyName + parent: netAppAccount + location: snapshotPolicyLocation + properties: { + enabled: snapEnabled + dailySchedule: { + hour: dailyHour + minute: dailyMinute + snapshotsToKeep: dailySnapshotsToKeep + usedBytes: dailyUsedBytes + } + hourlySchedule: { + minute: hourlyMinute + snapshotsToKeep: hourlySnapshotsToKeep + usedBytes: hourlyUsedBytes + } + monthlySchedule: { + daysOfMonth: daysOfMonth + hour: monthlyHour + minute: monthlyMinute + snapshotsToKeep: monthlySnapshotsToKeep + usedBytes: monthlyUsedBytes + } + weeklySchedule: { + day: weeklyDay + hour: weeklyHour + minute: weeklyMinute + snapshotsToKeep: weeklySnapshotsToKeep + usedBytes: weeklyUsedBytes + } + } +} + +resource backupPolicies 'Microsoft.NetApp/netAppAccounts/backupPolicies@2024-03-01' = if (backupEnabled) { + name: backupPolicyName + parent: netAppAccount + location: backupPolicyLocation + properties: { + dailyBackupsToKeep: dailyBackupsToKeep + enabled: backupEnabled + monthlyBackupsToKeep: monthlyBackupsToKeep + weeklyBackupsToKeep: weeklyBackupsToKeep + } +} + +resource backupVaults 'Microsoft.NetApp/netAppAccounts/backupVaults@2024-03-01' = if (backupEnabled) { + name: backupVaultName + parent: netAppAccount + location: backupVaultLocation + properties: {} +} + +resource backups 'Microsoft.NetApp/netAppAccounts/backupVaults/backups@2024-03-01' = if (backupEnabled) { + name: backupName + parent: backupVaults + properties: backupEnabled + ? { + label: backupLabel + snapshotName: snapshotName + useExistingSnapshot: useExistingSnapshot + volumeResourceId: volumeResourceId + } + : {} } resource volume_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(volume.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(volume.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -118,6 +362,9 @@ output location string = volume.location // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json index 2e8d146de2..968ee1993c 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "308407733537316660" + "version": "0.29.47.4906", + "templateHash": "6870432140728658293" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -18,6 +18,13 @@ "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": { @@ -93,6 +100,278 @@ "description": "Conditional. The name of the parent capacity pool. Required if the template is used in a standalone deployment." } }, + "coolAccess": { + "type": "bool", + "metadata": { + "description": "Optional. If enabled (true) the pool can contain cool Access enabled volumes." + } + }, + "coolnessPeriod": { + "type": "int", + "metadata": { + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." + } + }, + "coolAccessRetrievalPolicy": { + "type": "string", + "defaultValue": "Default", + "metadata": { + "description": "Optional. determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." + } + }, + "encryptionKeySource": { + "type": "string", + "metadata": { + "description": "Optional. The source of the encryption key." + } + }, + "keyVaultPrivateEndpointResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the key vault private endpoint." + } + }, + "endpointType": { + "type": "string", + "metadata": { + "description": "Optional. Indicates whether the local volume is the source or destination for the Volume Replication (src/dst)." + } + }, + "remoteVolumeRegion": { + "type": "string", + "metadata": { + "description": "Optional. The remote region for the other end of the Volume Replication." + } + }, + "remoteVolumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the remote volume." + } + }, + "replicationSchedule": { + "type": "string", + "metadata": { + "description": "Optional. The replication schedule for the volume." + } + }, + "backupEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the backup policy is enabled." + } + }, + "backupPolicyName": { + "type": "string", + "defaultValue": "backupPolicy", + "metadata": { + "description": "Optional. The name of the backup policy." + } + }, + "backupPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup policy." + } + }, + "dailyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The daily backups to keep." + } + }, + "monthlyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The monthly backups to keep." + } + }, + "weeklyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The weekly backups to keep." + } + }, + "backupVaultName": { + "type": "string", + "defaultValue": "vault", + "metadata": { + "description": "Optional. The name of the backup vault." + } + }, + "backupVaultLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup vault." + } + }, + "backupName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the backup." + } + }, + "backupLabel": { + "type": "string", + "metadata": { + "description": "Optional. The label of the backup." + } + }, + "useExistingSnapshot": { + "type": "bool", + "metadata": { + "description": "Optional. Indicates whether to use an existing snapshot." + } + }, + "snapshotName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot." + } + }, + "snapshotPolicyId": { + "type": "string", + "metadata": { + "description": "Optional. Snapshot Policy ResourceId." + } + }, + "snapshotPolicyName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot policy." + } + }, + "snapshotPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the snapshot policy." + } + }, + "dailyHour": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot hour." + } + }, + "dailyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot minute." + } + }, + "dailySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot count to keep." + } + }, + "dailyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot used bytes." + } + }, + "hourlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The hourly snapshot minute." + } + }, + "hourlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot count to keep." + } + }, + "hourlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot used bytes." + } + }, + "daysOfMonth": { + "type": "string", + "metadata": { + "description": "Optional. The monthly snapshot day." + } + }, + "monthlyHour": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot hour." + } + }, + "monthlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot minute." + } + }, + "monthlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot count to keep." + } + }, + "monthlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot used bytes." + } + }, + "weeklyDay": { + "type": "string", + "metadata": { + "description": "Optional. The weekly snapshot day." + } + }, + "weeklyHour": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot hour." + } + }, + "weeklyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot minute." + } + }, + "weeklySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot count to keep." + } + }, + "weeklyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot used bytes." + } + }, + "snapEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the snapshot policy is enabled." + } + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the volume." + } + }, + "volumeType": { + "type": "string", + "metadata": { + "description": "Optional. The type of the volume. DataProtection volumes are used for replication." + } + }, "name": { "type": "string", "metadata": { @@ -106,6 +385,15 @@ "description": "Optional. Location of the pool volume." } }, + "zones": { + "type": "array", + "defaultValue": [ + "1" + ], + "metadata": { + "description": "Optional. Zone where the volume will be placed." + } + }, "serviceLevel": { "type": "string", "defaultValue": "Standard", @@ -119,6 +407,19 @@ "description": "Optional. The pool service level. Must match the one of the parent capacity pool." } }, + "networkFeatures": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Basic_Standard", + "Standard", + "Standard_Basic" + ], + "metadata": { + "description": "Optional. Network feature for the volume." + } + }, "creationToken": { "type": "string", "defaultValue": "[parameters('name')]", @@ -160,6 +461,13 @@ } }, "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')]", @@ -172,7 +480,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]", "dependsOn": [ "netAppAccount" @@ -181,43 +489,112 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('netAppAccountName')]" }, "volume": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', parameters('keyVaultPrivateEndpointResourceId')), createObject()), if(not(equals(parameters('volumeType'), '')), createObject('volumeType', parameters('volumeType'), 'dataProtection', createObject('replication', createObject('endpointType', parameters('endpointType'), 'remoteVolumeRegion', parameters('remoteVolumeRegion'), 'remoteVolumeResourceId', parameters('remoteVolumeResourceId'), 'replicationSchedule', parameters('replicationSchedule')), 'snapshot', createObject('snapshotPolicyId', parameters('snapshotPolicyId')))), createObject()), createObject('networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', parameters('subnetResourceId'), 'exportPolicy', if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null()))))]", + "zones": "[parameters('zones')]", + "dependsOn": [ + "netAppAccount::capacityPool" + ] + }, + "snapshotPolicies": { + "condition": "[parameters('snapEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('snapshotPolicyName'))]", + "location": "[parameters('snapshotPolicyLocation')]", "properties": { - "serviceLevel": "[parameters('serviceLevel')]", - "creationToken": "[parameters('creationToken')]", - "usageThreshold": "[parameters('usageThreshold')]", - "protocolTypes": "[parameters('protocolTypes')]", - "subnetId": "[parameters('subnetResourceId')]", - "exportPolicy": "[if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null())]" + "enabled": "[parameters('snapEnabled')]", + "dailySchedule": { + "hour": "[parameters('dailyHour')]", + "minute": "[parameters('dailyMinute')]", + "snapshotsToKeep": "[parameters('dailySnapshotsToKeep')]", + "usedBytes": "[parameters('dailyUsedBytes')]" + }, + "hourlySchedule": { + "minute": "[parameters('hourlyMinute')]", + "snapshotsToKeep": "[parameters('hourlySnapshotsToKeep')]", + "usedBytes": "[parameters('hourlyUsedBytes')]" + }, + "monthlySchedule": { + "daysOfMonth": "[parameters('daysOfMonth')]", + "hour": "[parameters('monthlyHour')]", + "minute": "[parameters('monthlyMinute')]", + "snapshotsToKeep": "[parameters('monthlySnapshotsToKeep')]", + "usedBytes": "[parameters('monthlyUsedBytes')]" + }, + "weeklySchedule": { + "day": "[parameters('weeklyDay')]", + "hour": "[parameters('weeklyHour')]", + "minute": "[parameters('weeklyMinute')]", + "snapshotsToKeep": "[parameters('weeklySnapshotsToKeep')]", + "usedBytes": "[parameters('weeklyUsedBytes')]" + } }, "dependsOn": [ - "netAppAccount::capacityPool" + "netAppAccount" + ] + }, + "backupPolicies": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupPolicyName'))]", + "location": "[parameters('backupPolicyLocation')]", + "properties": { + "dailyBackupsToKeep": "[parameters('dailyBackupsToKeep')]", + "enabled": "[parameters('backupEnabled')]", + "monthlyBackupsToKeep": "[parameters('monthlyBackupsToKeep')]", + "weeklyBackupsToKeep": "[parameters('weeklyBackupsToKeep')]" + }, + "dependsOn": [ + "netAppAccount" + ] + }, + "backupVaults": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupVaultName'))]", + "location": "[parameters('backupVaultLocation')]", + "properties": {}, + "dependsOn": [ + "netAppAccount" + ] + }, + "backups": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults/backups", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('backupVaultName'), parameters('backupName'))]", + "properties": "[if(parameters('backupEnabled'), createObject('label', parameters('backupLabel'), 'snapshotName', parameters('snapshotName'), 'useExistingSnapshot', parameters('useExistingSnapshot'), 'volumeResourceId', parameters('volumeResourceId')), createObject())]", + "dependsOn": [ + "backupVaults" ] }, "volume_roleAssignments": { "copy": { "name": "volume_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}/capacityPools/{1}/volumes/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "volume" @@ -251,7 +628,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('volume', '2023-11-01', 'full').location]" + "value": "[reference('volume', '2024-03-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/net-app/net-app-account/main.bicep b/avm/res/net-app/net-app-account/main.bicep index 17c25a21e1..4f00ec4c93 100644 --- a/avm/res/net-app/net-app-account/main.bicep +++ b/avm/res/net-app/net-app-account/main.bicep @@ -5,6 +5,9 @@ metadata owner = 'Azure/module-maintainers' @description('Required. The name of the NetApp account.') param name string +@description('Optional. Name of the active directory host as part of Kerberos Realm used for Kerberos authentication.') +param adName string = '' + @description('Optional. Enable AES encryption on the SMB Server.') param aesEncryption bool = false @@ -27,6 +30,9 @@ param domainJoinOU string = '' @description('Optional. Required if domainName is specified. Comma separated list of DNS server IP addresses (IPv4 only) required for the Active Directory (AD) domain join and SMB authentication operations to succeed.') param dnsServers string = '' +@description('Optional. Specifies whether encryption should be used for communication between SMB server and domain controller (DC). SMB3 only.') +param encryptDCConnections bool = false + @description('Optional. Required if domainName is specified. NetBIOS name of the SMB server. A computer account with this prefix will be registered in the AD and used to mount volumes.') param smbServerNamePrefix string = '' @@ -39,6 +45,12 @@ param managedIdentities managedIdentitiesType @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType +@description('Optional. Kerberos Key Distribution Center (KDC) as part of Kerberos Realm used for Kerberos authentication.') +param kdcIP string = '' + +@description('Optional. Specifies whether to use TLS when NFS (with/without Kerberos) and SMB volumes communicate with an LDAP server. A server root CA certificate must be uploaded if enabled (serverRootCACertificate).') +param ldapOverTLS bool = false + @description('Optional. Specifies whether or not the LDAP traffic needs to be signed.') param ldapSigning bool = false @@ -48,6 +60,9 @@ param location string = resourceGroup().location @description('Optional. The lock settings of the service.') param lock lockType +@description('Optional. A server Root certificate is required of ldapOverTLS is enabled.') +param serverRootCACertificate string = '' + @description('Optional. Tags for all resources.') param tags object? @@ -56,12 +71,17 @@ param enableTelemetry bool = true var activeDirectoryConnectionProperties = [ { + adName: !empty(domainName) ? adName : null aesEncryption: !empty(domainName) ? aesEncryption : false username: !empty(domainName) ? domainJoinUser : null password: !empty(domainName) ? domainJoinPassword : null domain: !empty(domainName) ? domainName : null dns: !empty(domainName) ? dnsServers : null + encryptDCConnections: !empty(domainName) ? encryptDCConnections : false + kdcIP: !empty(domainName) ? kdcIP : null + ldapOverTLS: !empty(domainName) ? ldapOverTLS : false ldapSigning: !empty(domainName) ? ldapSigning : false + serverRootCACertificate: !empty(domainName) ? serverRootCACertificate : null smbServerName: !empty(domainName) ? smbServerNamePrefix : null organizationalUnit: !empty(domainJoinOU) ? domainJoinOU : null } @@ -94,8 +114,19 @@ var builtInRoleNames = { ) } +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)) + }) +] #disable-next-line no-deployments-resources -resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { name: '46d3xbcp.res.netapp-netappaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' properties: { mode: 'Incremental' @@ -133,7 +164,7 @@ resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentiti ) } -resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2023-11-01' = { +resource netAppAccount 'Microsoft.NetApp/netAppAccounts@2024-03-01' = { name: name tags: tags identity: identity @@ -170,14 +201,10 @@ resource netAppAccount_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!em } resource netAppAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(netAppAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(netAppAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -197,12 +224,12 @@ module netAppAccount_capacityPools 'capacity-pool/main.bicep' = [ name: capacityPool.name location: location size: capacityPool.size - serviceLevel: contains(capacityPool, 'serviceLevel') ? capacityPool.serviceLevel : 'Standard' - qosType: contains(capacityPool, 'qosType') ? capacityPool.qosType : 'Auto' - volumes: contains(capacityPool, 'volumes') ? capacityPool.volumes : [] - coolAccess: contains(capacityPool, 'coolAccess') ? capacityPool.coolAccess : false - roleAssignments: contains(capacityPool, 'roleAssignments') ? capacityPool.roleAssignments : [] - encryptionType: contains(capacityPool, 'encryptionType') ? capacityPool.encryptionType : 'Single' + serviceLevel: capacityPool.?serviceLevel ?? 'Standard' + qosType: capacityPool.?qosType ?? 'Auto' + volumes: capacityPool.?volumes ?? [] + coolAccess: capacityPool.?coolAccess ?? false + roleAssignments: capacityPool.?roleAssignments ?? [] + encryptionType: capacityPool.?encryptionType ?? 'Single' tags: capacityPool.?tags ?? tags } } @@ -220,6 +247,9 @@ output resourceGroupName string = resourceGroup().name @description('The location the resource was deployed into.') output location string = netAppAccount.location +@description('The resource IDs of the volume created in the capacity pool.') +output volumeResourceId string = (capacityPools != []) ? netAppAccount_capacityPools[0].outputs.volumeResourceId : '' + // =============== // // Definitions // // =============== // @@ -238,6 +268,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/net-app/net-app-account/main.json b/avm/res/net-app/net-app-account/main.json index 68d838dffb..73c191fcbd 100644 --- a/avm/res/net-app/net-app-account/main.json +++ b/avm/res/net-app/net-app-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3443173491807200896" + "version": "0.29.47.4906", + "templateHash": "4309703310956110255" }, "name": "Azure NetApp Files", "description": "This module deploys an Azure NetApp File.", @@ -58,6 +58,13 @@ "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": { @@ -159,6 +166,13 @@ "description": "Required. The name of the NetApp account." } }, + "adName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the active directory host as part of Kerberos Realm used for Kerberos authentication." + } + }, "aesEncryption": { "type": "bool", "defaultValue": false, @@ -207,6 +221,13 @@ "description": "Optional. Required if domainName is specified. Comma separated list of DNS server IP addresses (IPv4 only) required for the Active Directory (AD) domain join and SMB authentication operations to succeed." } }, + "encryptDCConnections": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether encryption should be used for communication between SMB server and domain controller (DC). SMB3 only." + } + }, "smbServerNamePrefix": { "type": "string", "defaultValue": "", @@ -233,6 +254,20 @@ "description": "Optional. Array of role assignments to create." } }, + "kdcIP": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Kerberos Key Distribution Center (KDC) as part of Kerberos Realm used for Kerberos authentication." + } + }, + "ldapOverTLS": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether to use TLS when NFS (with/without Kerberos) and SMB volumes communicate with an LDAP server. A server root CA certificate must be uploaded if enabled (serverRootCACertificate)." + } + }, "ldapSigning": { "type": "bool", "defaultValue": false, @@ -253,6 +288,13 @@ "description": "Optional. The lock settings of the service." } }, + "serverRootCACertificate": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. A server Root certificate is required of ldapOverTLS is enabled." + } + }, "tags": { "type": "object", "nullable": true, @@ -269,14 +311,26 @@ } }, "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)))))]" + } + ], "activeDirectoryConnectionProperties": [ { + "adName": "[if(not(empty(parameters('domainName'))), parameters('adName'), null())]", "aesEncryption": "[if(not(empty(parameters('domainName'))), parameters('aesEncryption'), false())]", "username": "[if(not(empty(parameters('domainName'))), parameters('domainJoinUser'), null())]", "password": "[if(not(empty(parameters('domainName'))), parameters('domainJoinPassword'), null())]", "domain": "[if(not(empty(parameters('domainName'))), parameters('domainName'), null())]", "dns": "[if(not(empty(parameters('domainName'))), parameters('dnsServers'), null())]", + "encryptDCConnections": "[if(not(empty(parameters('domainName'))), parameters('encryptDCConnections'), false())]", + "kdcIP": "[if(not(empty(parameters('domainName'))), parameters('kdcIP'), null())]", + "ldapOverTLS": "[if(not(empty(parameters('domainName'))), parameters('ldapOverTLS'), false())]", "ldapSigning": "[if(not(empty(parameters('domainName'))), parameters('ldapSigning'), false())]", + "serverRootCACertificate": "[if(not(empty(parameters('domainName'))), parameters('serverRootCACertificate'), null())]", "smbServerName": "[if(not(empty(parameters('domainName'))), parameters('smbServerNamePrefix'), null())]", "organizationalUnit": "[if(not(empty(parameters('domainJoinOU'))), parameters('domainJoinOU'), null())]" } @@ -307,7 +361,7 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", + "apiVersion": "2023-07-01", "name": "[format('46d3xbcp.res.netapp-netappaccount.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", @@ -344,7 +398,7 @@ }, "netAppAccount": { "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('name')]", "tags": "[parameters('tags')]", "identity": "[variables('identity')]", @@ -375,20 +429,20 @@ "netAppAccount_roleAssignments": { "copy": { "name": "netAppAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "netAppAccount" @@ -420,12 +474,24 @@ "size": { "value": "[parameters('capacityPools')[copyIndex()].size]" }, - "serviceLevel": "[if(contains(parameters('capacityPools')[copyIndex()], 'serviceLevel'), createObject('value', parameters('capacityPools')[copyIndex()].serviceLevel), createObject('value', 'Standard'))]", - "qosType": "[if(contains(parameters('capacityPools')[copyIndex()], 'qosType'), createObject('value', parameters('capacityPools')[copyIndex()].qosType), createObject('value', 'Auto'))]", - "volumes": "[if(contains(parameters('capacityPools')[copyIndex()], 'volumes'), createObject('value', parameters('capacityPools')[copyIndex()].volumes), createObject('value', createArray()))]", - "coolAccess": "[if(contains(parameters('capacityPools')[copyIndex()], 'coolAccess'), createObject('value', parameters('capacityPools')[copyIndex()].coolAccess), createObject('value', false()))]", - "roleAssignments": "[if(contains(parameters('capacityPools')[copyIndex()], 'roleAssignments'), createObject('value', parameters('capacityPools')[copyIndex()].roleAssignments), createObject('value', createArray()))]", - "encryptionType": "[if(contains(parameters('capacityPools')[copyIndex()], 'encryptionType'), createObject('value', parameters('capacityPools')[copyIndex()].encryptionType), createObject('value', 'Single'))]", + "serviceLevel": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'serviceLevel'), 'Standard')]" + }, + "qosType": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'qosType'), 'Auto')]" + }, + "volumes": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'volumes'), createArray())]" + }, + "coolAccess": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'coolAccess'), false())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'roleAssignments'), createArray())]" + }, + "encryptionType": { + "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'encryptionType'), 'Single')]" + }, "tags": { "value": "[coalesce(tryGet(parameters('capacityPools')[copyIndex()], 'tags'), parameters('tags'))]" } @@ -437,8 +503,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13254524547530770866" + "version": "0.29.47.4906", + "templateHash": "2991493712029822030" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool.", @@ -450,6 +516,13 @@ "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": { @@ -552,6 +625,13 @@ "description": "Optional. The pool service level." } }, + "networkFeatures": { + "type": "string", + "defaultValue": "Standard", + "metadata": { + "description": "Required. Network features available to the volume, or current state of update (Basic/Standard)." + } + }, "size": { "type": "int", "metadata": { @@ -602,6 +682,13 @@ } }, "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')]", @@ -614,12 +701,12 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('netAppAccountName')]" }, "capacityPool": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('name'))]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -637,20 +724,20 @@ "capacityPool_roleAssignments": { "copy": { "name": "capacityPool_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}/capacityPools/{1}', parameters('netAppAccountName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools', parameters('netAppAccountName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools', parameters('netAppAccountName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "capacityPool" @@ -687,16 +774,162 @@ "serviceLevel": { "value": "[parameters('serviceLevel')]" }, - "creationToken": "[if(contains(parameters('volumes')[copyIndex()], 'creationToken'), createObject('value', parameters('volumes')[copyIndex()].creationToken), createObject('value', parameters('volumes')[copyIndex()].name))]", + "creationToken": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'creationToken'), parameters('volumes')[copyIndex()].name)]" + }, "usageThreshold": { "value": "[parameters('volumes')[copyIndex()].usageThreshold]" }, - "protocolTypes": "[if(contains(parameters('volumes')[copyIndex()], 'protocolTypes'), createObject('value', parameters('volumes')[copyIndex()].protocolTypes), createObject('value', createArray()))]", + "protocolTypes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'protocolTypes'), createArray())]" + }, "subnetResourceId": { "value": "[parameters('volumes')[copyIndex()].subnetResourceId]" }, - "exportPolicyRules": "[if(contains(parameters('volumes')[copyIndex()], 'exportPolicyRules'), createObject('value', parameters('volumes')[copyIndex()].exportPolicyRules), createObject('value', createArray()))]", - "roleAssignments": "[if(contains(parameters('volumes')[copyIndex()], 'roleAssignments'), createObject('value', parameters('volumes')[copyIndex()].roleAssignments), createObject('value', createArray()))]" + "exportPolicyRules": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'exportPolicyRules'), createArray())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'roleAssignments'), createArray())]" + }, + "networkFeatures": { + "value": "[tryGet(parameters('volumes')[copyIndex()], 'networkFeatures')]" + }, + "zones": { + "value": "[tryGet(parameters('volumes')[copyIndex()], 'zones')]" + }, + "coolAccess": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolAccess'), false())]" + }, + "coolAccessRetrievalPolicy": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolAccessRetrievalPolicy'), 'Default')]" + }, + "coolnessPeriod": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'coolnessPeriod'), 0)]" + }, + "encryptionKeySource": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'encryptionKeySource'), 'Microsoft.NetApp')]" + }, + "keyVaultPrivateEndpointResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'keyVaultPrivateEndpointResourceId'), '')]" + }, + "endpointType": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'endpointType'), '')]" + }, + "remoteVolumeRegion": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'remoteVolumeRegion'), '')]" + }, + "remoteVolumeResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'remoteVolumeResourceId'), '')]" + }, + "replicationSchedule": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'replicationSchedule'), '')]" + }, + "snapshotPolicyId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyId'), '')]" + }, + "snapshotPolicyName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyName'), 'snapshotPolicy')]" + }, + "snapshotPolicyLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotPolicyLocation'), '')]" + }, + "snapEnabled": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapEnabled'), false())]" + }, + "dailyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyHour'), 0)]" + }, + "dailyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyMinute'), 0)]" + }, + "dailySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailySnapshotsToKeep'), 0)]" + }, + "dailyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyUsedBytes'), 0)]" + }, + "hourlyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlyMinute'), 0)]" + }, + "hourlySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlySnapshotsToKeep'), 0)]" + }, + "hourlyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'hourlyUsedBytes'), 0)]" + }, + "daysOfMonth": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'daysOfMonth'), '')]" + }, + "monthlyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyHour'), 0)]" + }, + "monthlyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyMinute'), 0)]" + }, + "monthlySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlySnapshotsToKeep'), 0)]" + }, + "monthlyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyUsedBytes'), 0)]" + }, + "weeklyDay": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyDay'), '')]" + }, + "weeklyHour": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyHour'), 0)]" + }, + "weeklyMinute": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyMinute'), 0)]" + }, + "weeklySnapshotsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklySnapshotsToKeep'), 0)]" + }, + "weeklyUsedBytes": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyUsedBytes'), 0)]" + }, + "backupPolicyName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupPolicyName'), 'backupPolicy')]" + }, + "backupPolicyLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupPolicyLocation'), '')]" + }, + "dailyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'dailyBackupsToKeep'), 0)]" + }, + "backupEnabled": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupEnabled'), false())]" + }, + "monthlyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'monthlyBackupsToKeep'), 0)]" + }, + "weeklyBackupsToKeep": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'weeklyBackupsToKeep'), 0)]" + }, + "backupVaultName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupVaultName'), 'vault')]" + }, + "backupVaultLocation": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupVaultLocation'), '')]" + }, + "backupName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupName'), 'backup')]" + }, + "backupLabel": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'backupLabel'), '')]" + }, + "snapshotName": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'snapshotName'), 'snapshot')]" + }, + "useExistingSnapshot": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'useExistingSnapshot'), false())]" + }, + "volumeResourceId": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'volumeResourceId'), '')]" + }, + "volumeType": { + "value": "[coalesce(tryGet(parameters('volumes')[copyIndex()], 'volumeType'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -705,8 +938,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "308407733537316660" + "version": "0.29.47.4906", + "templateHash": "6870432140728658293" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -718,6 +951,13 @@ "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": { @@ -793,6 +1033,278 @@ "description": "Conditional. The name of the parent capacity pool. Required if the template is used in a standalone deployment." } }, + "coolAccess": { + "type": "bool", + "metadata": { + "description": "Optional. If enabled (true) the pool can contain cool Access enabled volumes." + } + }, + "coolnessPeriod": { + "type": "int", + "metadata": { + "description": "Optional. Specifies the number of days after which data that is not accessed by clients will be tiered." + } + }, + "coolAccessRetrievalPolicy": { + "type": "string", + "defaultValue": "Default", + "metadata": { + "description": "Optional. determines the data retrieval behavior from the cool tier to standard storage based on the read pattern for cool access enabled volumes (Default/Never/Read)." + } + }, + "encryptionKeySource": { + "type": "string", + "metadata": { + "description": "Optional. The source of the encryption key." + } + }, + "keyVaultPrivateEndpointResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the key vault private endpoint." + } + }, + "endpointType": { + "type": "string", + "metadata": { + "description": "Optional. Indicates whether the local volume is the source or destination for the Volume Replication (src/dst)." + } + }, + "remoteVolumeRegion": { + "type": "string", + "metadata": { + "description": "Optional. The remote region for the other end of the Volume Replication." + } + }, + "remoteVolumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the remote volume." + } + }, + "replicationSchedule": { + "type": "string", + "metadata": { + "description": "Optional. The replication schedule for the volume." + } + }, + "backupEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the backup policy is enabled." + } + }, + "backupPolicyName": { + "type": "string", + "defaultValue": "backupPolicy", + "metadata": { + "description": "Optional. The name of the backup policy." + } + }, + "backupPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup policy." + } + }, + "dailyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The daily backups to keep." + } + }, + "monthlyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The monthly backups to keep." + } + }, + "weeklyBackupsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. The weekly backups to keep." + } + }, + "backupVaultName": { + "type": "string", + "defaultValue": "vault", + "metadata": { + "description": "Optional. The name of the backup vault." + } + }, + "backupVaultLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the backup vault." + } + }, + "backupName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the backup." + } + }, + "backupLabel": { + "type": "string", + "metadata": { + "description": "Optional. The label of the backup." + } + }, + "useExistingSnapshot": { + "type": "bool", + "metadata": { + "description": "Optional. Indicates whether to use an existing snapshot." + } + }, + "snapshotName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot." + } + }, + "snapshotPolicyId": { + "type": "string", + "metadata": { + "description": "Optional. Snapshot Policy ResourceId." + } + }, + "snapshotPolicyName": { + "type": "string", + "metadata": { + "description": "Optional. The name of the snapshot policy." + } + }, + "snapshotPolicyLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location of the snapshot policy." + } + }, + "dailyHour": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot hour." + } + }, + "dailyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The daily snapshot minute." + } + }, + "dailySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot count to keep." + } + }, + "dailyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Daily snapshot used bytes." + } + }, + "hourlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The hourly snapshot minute." + } + }, + "hourlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot count to keep." + } + }, + "hourlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Hourly snapshot used bytes." + } + }, + "daysOfMonth": { + "type": "string", + "metadata": { + "description": "Optional. The monthly snapshot day." + } + }, + "monthlyHour": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot hour." + } + }, + "monthlyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The monthly snapshot minute." + } + }, + "monthlySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot count to keep." + } + }, + "monthlyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Monthly snapshot used bytes." + } + }, + "weeklyDay": { + "type": "string", + "metadata": { + "description": "Optional. The weekly snapshot day." + } + }, + "weeklyHour": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot hour." + } + }, + "weeklyMinute": { + "type": "int", + "metadata": { + "description": "Optional. The weekly snapshot minute." + } + }, + "weeklySnapshotsToKeep": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot count to keep." + } + }, + "weeklyUsedBytes": { + "type": "int", + "metadata": { + "description": "Optional. Weekly snapshot used bytes." + } + }, + "snapEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether the snapshot policy is enabled." + } + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the volume." + } + }, + "volumeType": { + "type": "string", + "metadata": { + "description": "Optional. The type of the volume. DataProtection volumes are used for replication." + } + }, "name": { "type": "string", "metadata": { @@ -806,6 +1318,15 @@ "description": "Optional. Location of the pool volume." } }, + "zones": { + "type": "array", + "defaultValue": [ + "1" + ], + "metadata": { + "description": "Optional. Zone where the volume will be placed." + } + }, "serviceLevel": { "type": "string", "defaultValue": "Standard", @@ -819,6 +1340,19 @@ "description": "Optional. The pool service level. Must match the one of the parent capacity pool." } }, + "networkFeatures": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Basic_Standard", + "Standard", + "Standard_Basic" + ], + "metadata": { + "description": "Optional. Network feature for the volume." + } + }, "creationToken": { "type": "string", "defaultValue": "[parameters('name')]", @@ -860,6 +1394,13 @@ } }, "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')]", @@ -872,7 +1413,7 @@ "netAppAccount::capacityPool": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts/capacityPools", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('capacityPoolName'))]", "dependsOn": [ "netAppAccount" @@ -881,43 +1422,112 @@ "netAppAccount": { "existing": true, "type": "Microsoft.NetApp/netAppAccounts", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[parameters('netAppAccountName')]" }, "volume": { "type": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes", - "apiVersion": "2023-11-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", "location": "[parameters('location')]", + "properties": "[shallowMerge(createArray(createObject('coolAccess', parameters('coolAccess'), 'coolAccessRetrievalPolicy', parameters('coolAccessRetrievalPolicy'), 'coolnessPeriod', parameters('coolnessPeriod'), 'encryptionKeySource', parameters('encryptionKeySource')), if(not(equals(parameters('encryptionKeySource'), 'Microsoft.NetApp')), createObject('keyVaultPrivateEndpointResourceId', parameters('keyVaultPrivateEndpointResourceId')), createObject()), if(not(equals(parameters('volumeType'), '')), createObject('volumeType', parameters('volumeType'), 'dataProtection', createObject('replication', createObject('endpointType', parameters('endpointType'), 'remoteVolumeRegion', parameters('remoteVolumeRegion'), 'remoteVolumeResourceId', parameters('remoteVolumeResourceId'), 'replicationSchedule', parameters('replicationSchedule')), 'snapshot', createObject('snapshotPolicyId', parameters('snapshotPolicyId')))), createObject()), createObject('networkFeatures', parameters('networkFeatures'), 'serviceLevel', parameters('serviceLevel'), 'creationToken', parameters('creationToken'), 'usageThreshold', parameters('usageThreshold'), 'protocolTypes', parameters('protocolTypes'), 'subnetId', parameters('subnetResourceId'), 'exportPolicy', if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null()))))]", + "zones": "[parameters('zones')]", + "dependsOn": [ + "netAppAccount::capacityPool" + ] + }, + "snapshotPolicies": { + "condition": "[parameters('snapEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/snapshotPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('snapshotPolicyName'))]", + "location": "[parameters('snapshotPolicyLocation')]", "properties": { - "serviceLevel": "[parameters('serviceLevel')]", - "creationToken": "[parameters('creationToken')]", - "usageThreshold": "[parameters('usageThreshold')]", - "protocolTypes": "[parameters('protocolTypes')]", - "subnetId": "[parameters('subnetResourceId')]", - "exportPolicy": "[if(not(empty(parameters('exportPolicyRules'))), createObject('rules', parameters('exportPolicyRules')), null())]" + "enabled": "[parameters('snapEnabled')]", + "dailySchedule": { + "hour": "[parameters('dailyHour')]", + "minute": "[parameters('dailyMinute')]", + "snapshotsToKeep": "[parameters('dailySnapshotsToKeep')]", + "usedBytes": "[parameters('dailyUsedBytes')]" + }, + "hourlySchedule": { + "minute": "[parameters('hourlyMinute')]", + "snapshotsToKeep": "[parameters('hourlySnapshotsToKeep')]", + "usedBytes": "[parameters('hourlyUsedBytes')]" + }, + "monthlySchedule": { + "daysOfMonth": "[parameters('daysOfMonth')]", + "hour": "[parameters('monthlyHour')]", + "minute": "[parameters('monthlyMinute')]", + "snapshotsToKeep": "[parameters('monthlySnapshotsToKeep')]", + "usedBytes": "[parameters('monthlyUsedBytes')]" + }, + "weeklySchedule": { + "day": "[parameters('weeklyDay')]", + "hour": "[parameters('weeklyHour')]", + "minute": "[parameters('weeklyMinute')]", + "snapshotsToKeep": "[parameters('weeklySnapshotsToKeep')]", + "usedBytes": "[parameters('weeklyUsedBytes')]" + } }, "dependsOn": [ - "netAppAccount::capacityPool" + "netAppAccount" + ] + }, + "backupPolicies": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupPolicies", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupPolicyName'))]", + "location": "[parameters('backupPolicyLocation')]", + "properties": { + "dailyBackupsToKeep": "[parameters('dailyBackupsToKeep')]", + "enabled": "[parameters('backupEnabled')]", + "monthlyBackupsToKeep": "[parameters('monthlyBackupsToKeep')]", + "weeklyBackupsToKeep": "[parameters('weeklyBackupsToKeep')]" + }, + "dependsOn": [ + "netAppAccount" + ] + }, + "backupVaults": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}', parameters('netAppAccountName'), parameters('backupVaultName'))]", + "location": "[parameters('backupVaultLocation')]", + "properties": {}, + "dependsOn": [ + "netAppAccount" + ] + }, + "backups": { + "condition": "[parameters('backupEnabled')]", + "type": "Microsoft.NetApp/netAppAccounts/backupVaults/backups", + "apiVersion": "2024-03-01", + "name": "[format('{0}/{1}/{2}', parameters('netAppAccountName'), parameters('backupVaultName'), parameters('backupName'))]", + "properties": "[if(parameters('backupEnabled'), createObject('label', parameters('backupLabel'), 'snapshotName', parameters('snapshotName'), 'useExistingSnapshot', parameters('useExistingSnapshot'), 'volumeResourceId', parameters('volumeResourceId')), createObject())]", + "dependsOn": [ + "backupVaults" ] }, "volume_roleAssignments": { "copy": { "name": "volume_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.NetApp/netAppAccounts/{0}/capacityPools/{1}/volumes/{2}', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.NetApp/netAppAccounts/capacityPools/volumes', parameters('netAppAccountName'), parameters('capacityPoolName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "volume" @@ -951,7 +1561,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('volume', '2023-11-01', 'full').location]" + "value": "[reference('volume', '2024-03-01', 'full').location]" } } } @@ -989,7 +1599,14 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('capacityPool', '2023-11-01', 'full').location]" + "value": "[reference('capacityPool', '2024-03-01', 'full').location]" + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "The resource IDs of the volume created in the capacity pool." + }, + "value": "[if(not(equals(parameters('volumes'), createArray())), reference(format('capacityPool_volumes[{0}]', 0)).outputs.resourceId.value, '')]" } } } @@ -1026,7 +1643,14 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('netAppAccount', '2023-11-01', 'full').location]" + "value": "[reference('netAppAccount', '2024-03-01', 'full').location]" + }, + "volumeResourceId": { + "type": "string", + "metadata": { + "description": "The resource IDs of the volume created in the capacity pool." + }, + "value": "[if(not(equals(parameters('capacityPools'), createArray())), reference(format('netAppAccount_capacityPools[{0}]', 0)).outputs.volumeResourceId.value, '')]" } } } \ No newline at end of file diff --git a/avm/res/net-app/net-app-account/tests/e2e/defaults/main.test.bicep b/avm/res/net-app/net-app-account/tests/e2e/defaults/main.test.bicep index 80f9d4cbbc..697b4d5e96 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/defaults/main.test.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/defaults/main.test.bicep @@ -11,8 +11,9 @@ metadata description = 'This instance deploys the module with the minimum set of @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-netapp.netappaccounts-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// enforcing location due to quote restrictions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'australiaeast' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'nanaamin' @@ -28,7 +29,7 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -37,9 +38,9 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation } } diff --git a/avm/res/net-app/net-app-account/tests/e2e/max/dependencies.bicep b/avm/res/net-app/net-app-account/tests/e2e/max/dependencies.bicep index d0c6383547..b4dd5e4570 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/max/dependencies.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/max/dependencies.bicep @@ -1,5 +1,5 @@ -@description('Optional. The location to deploy to.') -param location string = resourceGroup().location +@description('Required. The location to create the resources.') +param location string @description('Required. The name of the Virtual Network to create.') param virtualNetworkName string diff --git a/avm/res/net-app/net-app-account/tests/e2e/max/main.test.bicep b/avm/res/net-app/net-app-account/tests/e2e/max/main.test.bicep index 0cdbb15426..0dc7ad7b1f 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/max/main.test.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/max/main.test.bicep @@ -11,8 +11,9 @@ metadata description = 'This instance deploys the module with most of its featur @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-netapp.netappaccounts-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// enforcing location due to quote restrictions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'australiaeast' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'nanaamax' @@ -20,6 +21,9 @@ param serviceShort string = 'nanaamax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Optional. The source of the encryption key.') +param encryptionKeySource string = 'Microsoft.NetApp' + // ============ // // Dependencies // // ============ // @@ -28,16 +32,16 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -47,10 +51,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation capacityPools: [ { name: '${namePrefix}-${serviceShort}-cp-001' @@ -76,6 +80,9 @@ module testDeployment '../../../main.bicep' = { } ] name: '${namePrefix}-${serviceShort}-vol-001' + zones: ['1'] + networkFeatures: 'Standard' + encryptionKeySource: encryptionKeySource protocolTypes: [ 'NFSv4.1' ] @@ -101,6 +108,9 @@ module testDeployment '../../../main.bicep' = { } ] name: '${namePrefix}-${serviceShort}-vol-002' + zones: ['1'] + networkFeatures: 'Standard' + encryptionKeySource: encryptionKeySource protocolTypes: [ 'NFSv4.1' ] @@ -125,11 +135,13 @@ module testDeployment '../../../main.bicep' = { ] roleAssignments: [ { + name: '18051111-2a33-4f8e-8b24-441aac1e6562' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/net-app/net-app-account/tests/e2e/nfs3/dependencies.bicep b/avm/res/net-app/net-app-account/tests/e2e/nfs3/dependencies.bicep index 71e1d77e16..3692fea512 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/nfs3/dependencies.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/nfs3/dependencies.bicep @@ -1,5 +1,5 @@ -@description('Optional. The location to deploy to.') -param location string = resourceGroup().location +@description('Required. The location to create the resources.') +param location string @description('Required. The name of the Virtual Network to create.') param virtualNetworkName string diff --git a/avm/res/net-app/net-app-account/tests/e2e/nfs3/main.test.bicep b/avm/res/net-app/net-app-account/tests/e2e/nfs3/main.test.bicep index d560257581..2a49696c4e 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/nfs3/main.test.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/nfs3/main.test.bicep @@ -11,8 +11,9 @@ metadata description = 'This instance deploys the module with nfs31.' @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-netapp.netappaccounts-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// enforcing location due to quote restrictions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'australiaeast' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'nanaanfs3' @@ -20,6 +21,9 @@ param serviceShort string = 'nanaanfs3' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +@description('Optional. The source of the encryption key.') +param encryptionKeySource string = 'Microsoft.NetApp' + // ============ // // Dependencies // // ============ // @@ -28,16 +32,16 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -47,10 +51,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation capacityPools: [ { name: '${namePrefix}-${serviceShort}-cp-001' @@ -76,6 +80,9 @@ module testDeployment '../../../main.bicep' = { } ] name: '${namePrefix}-${serviceShort}-vol-001' + zones: ['1'] + networkFeatures: 'Standard' + encryptionKeySource: encryptionKeySource protocolTypes: [ 'NFSv3' ] @@ -91,6 +98,9 @@ module testDeployment '../../../main.bicep' = { } { name: '${namePrefix}-${serviceShort}-vol-002' + zones: ['1'] + networkFeatures: 'Standard' + encryptionKeySource: encryptionKeySource protocolTypes: [ 'NFSv3' ] diff --git a/avm/res/net-app/net-app-account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/net-app/net-app-account/tests/e2e/waf-aligned/main.test.bicep index 0db581db88..9875a68757 100644 --- a/avm/res/net-app/net-app-account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/net-app/net-app-account/tests/e2e/waf-aligned/main.test.bicep @@ -12,8 +12,9 @@ metadata description = 'This instance deploys the module in alignment with the b // e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-netapp.netappaccounts-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +// enforcing location due to quote restrictions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'australiaeast' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') // e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test @@ -30,21 +31,24 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation + tags: { + service: 'netapp' + } } // ============== // // Test Execution // // ============== // -@batchSize(1) -module testDeployment '../../../main.bicep' = [ - for iteration in ['init', 'idem']: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: enforcedLocation + tags: { + service: 'netapp' } } -] +} diff --git a/avm/res/net-app/net-app-account/version.json b/avm/res/net-app/net-app-account/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/net-app/net-app-account/version.json +++ b/avm/res/net-app/net-app-account/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/application-gateway/README.md b/avm/res/network/application-gateway/README.md index f3faed0358..bcfdeb7ef5 100644 --- a/avm/res/network/application-gateway/README.md +++ b/avm/res/network/application-gateway/README.md @@ -19,8 +19,8 @@ This module deploys a Network Application Gateway. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/applicationGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/applicationGateways) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | ## Usage examples @@ -127,14 +127,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'public' subnetResourceId: '' tags: { @@ -662,11 +646,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -968,9 +954,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "public", "subnetResourceId": "", "tags": { @@ -1159,11 +1149,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1317,6 +1309,7 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' + firewallPolicyResourceId: '' frontendIPConfigurations: [ { name: 'private' @@ -1453,9 +1446,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'public' subnetResourceId: '' tags: { @@ -1644,20 +1641,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:" }, + "firewallPolicyResourceId": { + "value": "" + }, "frontendIPConfigurations": { "value": [ { @@ -1903,9 +1889,13 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "public", "subnetResourceId": "", "tags": { @@ -2110,24 +2100,6 @@ module applicationGateway 'br/public:avm/res/network/application-gateway:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -155,11 +157,13 @@ module applicationSecurityGroup 'br/public:avm/res/network/application-security- "roleAssignments": { "value": [ { + "name": "e9e73878-302e-4e67-a2f8-981ea073bdf7", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -355,6 +359,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -405,6 +410,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/application-security-group/main.bicep b/avm/res/network/application-security-group/main.bicep index 881c8a9218..d5e5d98046 100644 --- a/avm/res/network/application-security-group/main.bicep +++ b/avm/res/network/application-security-group/main.bicep @@ -34,6 +34,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-applicationsecuritygroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -72,14 +83,14 @@ resource applicationSecurityGroup_lock 'Microsoft.Authorization/locks@2020-05-01 } resource applicationSecurityGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(applicationSecurityGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + applicationSecurityGroup.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -116,6 +127,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/application-security-group/main.json b/avm/res/network/application-security-group/main.json index 7da9876d29..1eeeb5c6af 100644 --- a/avm/res/network/application-security-group/main.json +++ b/avm/res/network/application-security-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "734941481544270744" + "version": "0.29.47.4906", + "templateHash": "16774487248704034776" }, "name": "Application Security Groups (ASG)", "description": "This module deploys an Application Security Group (ASG).", @@ -43,6 +43,13 @@ "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": { @@ -147,6 +154,13 @@ } }, "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')]", @@ -201,20 +215,20 @@ "applicationSecurityGroup_roleAssignments": { "copy": { "name": "applicationSecurityGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/applicationSecurityGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/applicationSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/applicationSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "applicationSecurityGroup" diff --git a/avm/res/network/application-security-group/tests/e2e/max/main.test.bicep b/avm/res/network/application-security-group/tests/e2e/max/main.test.bicep index 773b0b4e2f..f3151ede43 100644 --- a/avm/res/network/application-security-group/tests/e2e/max/main.test.bicep +++ b/avm/res/network/application-security-group/tests/e2e/max/main.test.bicep @@ -58,11 +58,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'e9e73878-302e-4e67-a2f8-981ea073bdf7' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/azure-firewall/README.md b/avm/res/network/azure-firewall/README.md index 33d03091d5..26314b511b 100644 --- a/avm/res/network/azure-firewall/README.md +++ b/avm/res/network/azure-firewall/README.md @@ -30,13 +30,14 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/azure-firewall:`. - [Add-PIP](#example-1-add-pip) -- [Custom-PIP](#example-2-custom-pip) -- [Using only defaults](#example-3-using-only-defaults) -- [Hub-commom](#example-4-hub-commom) -- [Hub-min](#example-5-hub-min) -- [Using large parameter set](#example-6-using-large-parameter-set) -- [Public-IP-Prefix](#example-7-public-ip-prefix) -- [WAF-aligned](#example-8-waf-aligned) +- [Basic SKU](#example-2-basic-sku) +- [Custom-PIP](#example-3-custom-pip) +- [Using only defaults](#example-4-using-only-defaults) +- [Hub-commom](#example-5-hub-commom) +- [Hub-min](#example-6-hub-min) +- [Using large parameter set](#example-7-using-large-parameter-set) +- [Public-IP-Prefix](#example-8-public-ip-prefix) +- [WAF-aligned](#example-9-waf-aligned) ### Example 1: _Add-PIP_ @@ -130,7 +131,71 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 2: _Custom-PIP_ +### Example 2: _Basic SKU_ + +This instance deploys the module with the Basic SKU. + + +

+ +via Bicep module + +```bicep +module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { + name: 'azureFirewallDeployment' + params: { + // Required parameters + name: 'nafbasic001' + // Non-required parameters + azureSkuTier: 'Basic' + location: '' + networkRuleCollections: [] + threatIntelMode: 'Deny' + virtualNetworkResourceId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nafbasic001" + }, + // Non-required parameters + "azureSkuTier": { + "value": "Basic" + }, + "location": { + "value": "" + }, + "networkRuleCollections": { + "value": [] + }, + "threatIntelMode": { + "value": "Deny" + }, + "virtualNetworkResourceId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Custom-PIP_ This instance deploys the module and will create a public IP address. @@ -240,7 +305,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 3: _Using only defaults_ +### Example 4: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -292,7 +357,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 4: _Hub-commom_ +### Example 5: _Hub-commom_ This instance deploys the module a vWAN in a typical hub setting. @@ -360,7 +425,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 5: _Hub-min_ +### Example 6: _Hub-min_ This instance deploys the module a vWAN minimum hub setting. @@ -424,7 +489,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 6: _Using large parameter set_ +### Example 7: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -559,11 +624,13 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { publicIPResourceID: '' roleAssignments: [ { + name: '3a8da184-d6d8-4bea-b992-e27cc053ef21' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -738,11 +805,13 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "roleAssignments": { "value": [ { + "name": "3a8da184-d6d8-4bea-b992-e27cc053ef21", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -778,7 +847,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 7: _Public-IP-Prefix_ +### Example 8: _Public-IP-Prefix_ This instance deploys the module and will use a public IP prefix. @@ -870,7 +939,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = {

-### Example 8: _WAF-aligned_ +### Example 9: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -2027,6 +2096,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2077,6 +2147,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2153,7 +2230,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/public-ip-address:0.4.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/network/azure-firewall/main.bicep b/avm/res/network/azure-firewall/main.bicep index feb0af59eb..50709a437f 100644 --- a/avm/res/network/azure-firewall/main.bicep +++ b/avm/res/network/azure-firewall/main.bicep @@ -137,21 +137,15 @@ var ipConfigurations = concat( var managementIPConfiguration = { name: !empty(managementIPResourceID) ? last(split(managementIPResourceID, '/')) : managementIPAddress.outputs.name - properties: union( - { - subnet: { - id: '${virtualNetworkResourceId}/subnets/AzureFirewallManagementSubnet' // The subnet name must be AzureFirewallManagementSubnet for a 'Basic' SKU tier firewall - } - }, - (!empty(managementIPResourceID) || !empty(managementIPAddressObject)) - ? { - // Use existing Management Public IP, new Management Public IP created in this module, or none if neither - publicIPAddress: { - id: !empty(managementIPResourceID) ? managementIPResourceID : managementIPAddress.outputs.resourceId - } - } - : {} - ) + properties: { + subnet: { + id: '${virtualNetworkResourceId}/subnets/AzureFirewallManagementSubnet' // The subnet name must be AzureFirewallManagementSubnet for a 'Basic' SKU tier firewall + } + // Use existing Management Public IP, new Management Public IP created in this module, or none if neither + publicIPAddress: { + id: !empty(managementIPResourceID) ? managementIPResourceID : managementIPAddress.outputs.resourceId + } + } } // ---------------------------------------------------------------------------- @@ -169,6 +163,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-azurefirewall.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -188,7 +193,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if (empty(publicIPResourceID) && azureSkuName == 'AZFW_VNet') { +module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.5.1' = if (empty(publicIPResourceID) && azureSkuName == 'AZFW_VNet') { name: '${uniqueString(deployment().name, location)}-Firewall-PIP' params: { name: publicIPAddressObject.name @@ -219,7 +224,7 @@ module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if } // create a Management Public IP address if one is not provided and the flag is true -module managementIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if (isCreateDefaultManagementIP && azureSkuName == 'AZFW_VNet') { +module managementIPAddress 'br/public:avm/res/network/public-ip-address:0.5.1' = if (isCreateDefaultManagementIP && azureSkuName == 'AZFW_VNet') { name: '${uniqueString(deployment().name, location)}-Firewall-MIP' params: { name: contains(managementIPAddressObject, 'name') @@ -335,14 +340,10 @@ resource azureFirewall_diagnosticSettings 'Microsoft.Insights/diagnosticSettings ] resource azureFirewall_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(azureFirewall.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(azureFirewall.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -398,6 +399,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/azure-firewall/main.json b/avm/res/network/azure-firewall/main.json index 7b22e19797..b86274a64e 100644 --- a/avm/res/network/azure-firewall/main.json +++ b/avm/res/network/azure-firewall/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8740531791937494610" + "version": "0.29.47.4906", + "templateHash": "4172708261226601418" }, "name": "Azure Firewalls", "description": "This module deploys an Azure Firewall.", @@ -43,6 +43,13 @@ "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": { @@ -835,6 +842,11 @@ "publicIPAddress": "[if(contains(parameters('additionalPublicIpConfigurations')[copyIndex('additionalPublicIpConfigurationsVar')], 'publicIPAddressResourceId'), createObject('id', parameters('additionalPublicIpConfigurations')[copyIndex('additionalPublicIpConfigurationsVar')].publicIPAddressResourceId), null())]" } } + }, + { + "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)))))]" } ], "azureSkuName": "[if(empty(parameters('virtualNetworkResourceId')), 'AZFW_Hub', 'AZFW_VNet')]", @@ -876,7 +888,7 @@ "location": "[parameters('location')]", "zones": "[if(equals(length(parameters('zones')), 0), null(), parameters('zones'))]", "tags": "[parameters('tags')]", - "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('managementIPResourceID'))), not(empty(parameters('managementIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value))), createObject()))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]", + "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId'))), 'publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value)))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]", "dependsOn": [ "managementIPAddress", "publicIPAddress" @@ -940,20 +952,20 @@ "azureFirewall_roleAssignments": { "copy": { "name": "azureFirewall_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/azureFirewalls/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/azureFirewalls', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/azureFirewalls', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "azureFirewall" @@ -1004,8 +1016,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4718335757080871925" + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -1017,6 +1029,13 @@ "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": { @@ -1154,8 +1173,9 @@ } } }, + "nullable": true, "metadata": { - "description": "Required. The DDoS protection plan associated with the public IP address." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -1429,6 +1449,13 @@ } }, "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')]", @@ -1446,8 +1473,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1501,20 +1528,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1642,8 +1669,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4718335757080871925" + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -1655,6 +1682,13 @@ "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": { @@ -1792,8 +1826,9 @@ } } }, + "nullable": true, "metadata": { - "description": "Required. The DDoS protection plan associated with the public IP address." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -2067,6 +2102,13 @@ } }, "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')]", @@ -2084,8 +2126,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2139,20 +2181,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/azure-firewall/tests/e2e/addpip/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/addpip/dependencies.bicep index cf2cb1747a..7e2090e896 100644 --- a/avm/res/network/azure-firewall/tests/e2e/addpip/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/addpip/dependencies.bicep @@ -25,13 +25,13 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 20, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } { name: 'AzureFirewallManagementSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 20, 1) + addressPrefix: cidrSubnet(addressPrefix, 26, 1) } } ] diff --git a/avm/res/network/azure-firewall/tests/e2e/basic/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/basic/dependencies.bicep new file mode 100644 index 0000000000..5fcd99bfb8 --- /dev/null +++ b/avm/res/network/azure-firewall/tests/e2e/basic/dependencies.bicep @@ -0,0 +1,35 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 26, 0) + } + } + { + name: 'AzureFirewallManagementSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 26, 1) + } + } + ] + } +} +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/avm/res/network/azure-firewall/tests/e2e/basic/main.test.bicep b/avm/res/network/azure-firewall/tests/e2e/basic/main.test.bicep new file mode 100644 index 0000000000..30fd9d9e44 --- /dev/null +++ b/avm/res/network/azure-firewall/tests/e2e/basic/main.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +metadata name = 'Basic SKU' +metadata description = 'This instance deploys the module with the Basic SKU.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nafbasic' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + azureSkuTier: 'Basic' + virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + location: resourceLocation + threatIntelMode: 'Deny' + networkRuleCollections: [] + } + } +] diff --git a/avm/res/network/azure-firewall/tests/e2e/custompip/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/custompip/dependencies.bicep index 5d14b0b91c..b2b179a6c9 100644 --- a/avm/res/network/azure-firewall/tests/e2e/custompip/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/custompip/dependencies.bicep @@ -22,7 +22,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } ] diff --git a/avm/res/network/azure-firewall/tests/e2e/defaults/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/defaults/dependencies.bicep index 4d1cd2e6aa..fed44e2c97 100644 --- a/avm/res/network/azure-firewall/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/defaults/dependencies.bicep @@ -19,7 +19,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } ] diff --git a/avm/res/network/azure-firewall/tests/e2e/max/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/max/dependencies.bicep index de9bfec4ea..4f274af14a 100644 --- a/avm/res/network/azure-firewall/tests/e2e/max/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/max/dependencies.bicep @@ -25,7 +25,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } ] diff --git a/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep b/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep index 07ce848092..642c4b1d4c 100644 --- a/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep @@ -187,11 +187,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '3a8da184-d6d8-4bea-b992-e27cc053ef21' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep index fc6a624751..596fcd1257 100644 --- a/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/publicipprefix/dependencies.bicep @@ -22,13 +22,13 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 20, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } { name: 'AzureFirewallManagementSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 20, 1) + addressPrefix: cidrSubnet(addressPrefix, 26, 1) } } ] diff --git a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep index 4e6ccd3d21..b545a6b726 100644 --- a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep @@ -25,7 +25,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { { name: 'AzureFirewallSubnet' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 26, 0) } } ] diff --git a/avm/res/network/azure-firewall/version.json b/avm/res/network/azure-firewall/version.json index b3d560b1ad..3f863a2bec 100644 --- a/avm/res/network/azure-firewall/version.json +++ b/avm/res/network/azure-firewall/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/bastion-host/README.md b/avm/res/network/bastion-host/README.md index c3134f3fbe..15dd0f846c 100644 --- a/avm/res/network/bastion-host/README.md +++ b/avm/res/network/bastion-host/README.md @@ -254,11 +254,13 @@ module bastionHost 'br/public:avm/res/network/bastion-host:' = { } roleAssignments: [ { + name: 'a9329bd8-d7c8-4915-9dfe-04197fa5bf45' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -338,11 +340,13 @@ module bastionHost 'br/public:avm/res/network/bastion-host:' = { "roleAssignments": { "value": [ { + "name": "a9329bd8-d7c8-4915-9dfe-04197fa5bf45", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -777,6 +781,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -827,6 +832,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -891,7 +903,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/public-ip-address:0.4.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/network/bastion-host/main.bicep b/avm/res/network/bastion-host/main.bicep index 0c9949b902..75b7fbe9a1 100644 --- a/avm/res/network/bastion-host/main.bicep +++ b/avm/res/network/bastion-host/main.bicep @@ -100,6 +100,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-bastionhost.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -119,7 +130,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.4.0' = if (empty(bastionSubnetPublicIpResourceId)) { +module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.5.1' = if (empty(bastionSubnetPublicIpResourceId)) { name: '${uniqueString(deployment().name, location)}-Bastion-PIP' params: { name: publicIPAddressObject.name @@ -199,14 +210,10 @@ resource azureBastion_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@ ] resource azureBastion_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(azureBastion.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(azureBastion.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -246,6 +253,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/bastion-host/main.json b/avm/res/network/bastion-host/main.json index 0089bf6af0..75a5719bf3 100644 --- a/avm/res/network/bastion-host/main.json +++ b/avm/res/network/bastion-host/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7822017473517560623" + "version": "0.29.47.4906", + "templateHash": "11950325802419024259" }, "name": "Bastion Hosts", "description": "This module deploys a Bastion Host.", @@ -43,6 +43,13 @@ "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": { @@ -323,6 +330,13 @@ } }, "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')]", @@ -415,20 +429,20 @@ "azureBastion_roleAssignments": { "copy": { "name": "azureBastion_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/bastionHosts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -492,8 +506,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4718335757080871925" + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -505,6 +519,13 @@ "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": { @@ -642,8 +663,9 @@ } } }, + "nullable": true, "metadata": { - "description": "Required. The DDoS protection plan associated with the public IP address." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -917,6 +939,13 @@ } }, "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')]", @@ -934,8 +963,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -989,20 +1018,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep b/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep index 9ade33f499..9fe5b24a03 100644 --- a/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep +++ b/avm/res/network/bastion-host/tests/e2e/max/main.test.bicep @@ -89,11 +89,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'a9329bd8-d7c8-4915-9dfe-04197fa5bf45' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/bastion-host/version.json b/avm/res/network/bastion-host/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/bastion-host/version.json +++ b/avm/res/network/bastion-host/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/connection/README.md b/avm/res/network/connection/README.md index 918116f245..501da40d9b 100644 --- a/avm/res/network/connection/README.md +++ b/avm/res/network/connection/README.md @@ -333,6 +333,7 @@ module connection 'br/public:avm/res/network/connection:' = { | [`peer`](#parameter-peer) | object | The remote peer. Used for connection connectionType [ExpressRoute]. | | [`routingWeight`](#parameter-routingweight) | int | The weight added to routes learned from this BGP speaker. | | [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`trafficSelectorPolicies`](#parameter-trafficselectorpolicies) | array | The traffic selector policies to be considered by this connection. | | [`useLocalAzureIpAddress`](#parameter-uselocalazureipaddress) | bool | Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property. | | [`usePolicyBasedTrafficSelectors`](#parameter-usepolicybasedtrafficselectors) | bool | Enable policy-based traffic selectors. | | [`virtualNetworkGateway2`](#parameter-virtualnetworkgateway2) | object | The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet]. | @@ -542,6 +543,14 @@ Tags of the resource. - Required: No - Type: object +### Parameter: `trafficSelectorPolicies` + +The traffic selector policies to be considered by this connection. + +- Required: No +- Type: array +- Default: `[]` + ### Parameter: `useLocalAzureIpAddress` Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property. diff --git a/avm/res/network/connection/main.bicep b/avm/res/network/connection/main.bicep index 7521aa229f..28078935b7 100644 --- a/avm/res/network/connection/main.bicep +++ b/avm/res/network/connection/main.bicep @@ -47,6 +47,9 @@ param dpdTimeoutSeconds int = 45 @description('Optional. Enable policy-based traffic selectors.') param usePolicyBasedTrafficSelectors bool = false +@description('Optional. The traffic selector policies to be considered by this connection.') +param trafficSelectorPolicies array = [] + @description('Optional. Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route.') param enablePrivateLinkFastPath bool = false @@ -132,6 +135,7 @@ resource connection 'Microsoft.Network/connections@2023-04-01' = { peer: connectionType == 'ExpressRoute' ? peer : null authorizationKey: connectionType == 'ExpressRoute' && !empty(authorizationKey) ? authorizationKey : null sharedKey: connectionType != 'ExpressRoute' ? vpnSharedKey : null + trafficSelectorPolicies: trafficSelectorPolicies usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors ipsecPolicies: !empty(customIPSecPolicy.ipsecEncryption) ? [ diff --git a/avm/res/network/connection/main.json b/avm/res/network/connection/main.json index f92bc886ea..ec725f9c35 100644 --- a/avm/res/network/connection/main.json +++ b/avm/res/network/connection/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16458068655280591810" + "version": "0.29.47.4906", + "templateHash": "13075925260036107325" }, "name": "Virtual Network Gateway Connections", "description": "This module deploys a Virtual Network Gateway Connection.", @@ -119,6 +119,13 @@ "description": "Optional. Enable policy-based traffic selectors." } }, + "trafficSelectorPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The traffic selector policies to be considered by this connection." + } + }, "enablePrivateLinkFastPath": { "type": "bool", "defaultValue": false, @@ -258,6 +265,7 @@ "peer": "[if(equals(parameters('connectionType'), 'ExpressRoute'), parameters('peer'), null())]", "authorizationKey": "[if(and(equals(parameters('connectionType'), 'ExpressRoute'), not(empty(parameters('authorizationKey')))), parameters('authorizationKey'), null())]", "sharedKey": "[if(not(equals(parameters('connectionType'), 'ExpressRoute')), parameters('vpnSharedKey'), null())]", + "trafficSelectorPolicies": "[parameters('trafficSelectorPolicies')]", "usePolicyBasedTrafficSelectors": "[parameters('usePolicyBasedTrafficSelectors')]", "ipsecPolicies": "[if(not(empty(parameters('customIPSecPolicy').ipsecEncryption)), createArray(createObject('saLifeTimeSeconds', parameters('customIPSecPolicy').saLifeTimeSeconds, 'saDataSizeKilobytes', parameters('customIPSecPolicy').saDataSizeKilobytes, 'ipsecEncryption', parameters('customIPSecPolicy').ipsecEncryption, 'ipsecIntegrity', parameters('customIPSecPolicy').ipsecIntegrity, 'ikeEncryption', parameters('customIPSecPolicy').ikeEncryption, 'ikeIntegrity', parameters('customIPSecPolicy').ikeIntegrity, 'dhGroup', parameters('customIPSecPolicy').dhGroup, 'pfsGroup', parameters('customIPSecPolicy').pfsGroup)), parameters('customIPSecPolicy').ipsecEncryption)]", "routingWeight": "[parameters('routingWeight')]", diff --git a/avm/res/network/ddos-protection-plan/README.md b/avm/res/network/ddos-protection-plan/README.md index 0d9ebe18a1..ccb76c090c 100644 --- a/avm/res/network/ddos-protection-plan/README.md +++ b/avm/res/network/ddos-protection-plan/README.md @@ -102,11 +102,13 @@ module ddosProtectionPlan 'br/public:avm/res/network/ddos-protection-plan:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -155,11 +157,13 @@ module ddosProtectionPlan 'br/public:avm/res/network/ddos-protection-plan:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -355,6 +359,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -405,6 +410,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/ddos-protection-plan/main.bicep b/avm/res/network/ddos-protection-plan/main.bicep index caebe4df3e..915d9d5ad5 100644 --- a/avm/res/network/ddos-protection-plan/main.bicep +++ b/avm/res/network/ddos-protection-plan/main.bicep @@ -35,6 +35,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-ddosprotectionplan.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -73,14 +84,14 @@ resource ddosProtectionPlan_lock 'Microsoft.Authorization/locks@2020-05-01' = if } resource ddosProtectionPlan_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(ddosProtectionPlan.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + ddosProtectionPlan.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -117,6 +128,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/ddos-protection-plan/main.json b/avm/res/network/ddos-protection-plan/main.json index 2f2d180660..622b0b8632 100644 --- a/avm/res/network/ddos-protection-plan/main.json +++ b/avm/res/network/ddos-protection-plan/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14407264854756199224" + "version": "0.29.47.4906", + "templateHash": "9448953054880328206" }, "name": "DDoS Protection Plans", "description": "This module deploys a DDoS Protection Plan.", @@ -43,6 +43,13 @@ "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": { @@ -148,6 +155,13 @@ } }, "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')]", @@ -202,20 +216,20 @@ "ddosProtectionPlan_roleAssignments": { "copy": { "name": "ddosProtectionPlan_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/ddosProtectionPlans/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/ddosProtectionPlans', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/ddosProtectionPlans', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "ddosProtectionPlan" diff --git a/avm/res/network/ddos-protection-plan/tests/e2e/max/main.test.bicep b/avm/res/network/ddos-protection-plan/tests/e2e/max/main.test.bicep index 7f279ff62d..2f167d322b 100644 --- a/avm/res/network/ddos-protection-plan/tests/e2e/max/main.test.bicep +++ b/avm/res/network/ddos-protection-plan/tests/e2e/max/main.test.bicep @@ -11,9 +11,6 @@ metadata description = 'This instance deploys the module with most of its featur @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-network.ddosprotectionplans-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'ndppmax' @@ -21,7 +18,7 @@ param serviceShort string = 'ndppmax' param namePrefix string = '#_namePrefix_#' // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription -#disable-next-line no-hardcoded-location +#disable-next-line no-hardcoded-location var enforcedLocation = 'northeurope' // ============ // @@ -60,11 +57,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: '60339368-138d-4667-988a-5431c156f6ff' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/ddos-protection-plan/version.json b/avm/res/network/ddos-protection-plan/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/network/ddos-protection-plan/version.json +++ b/avm/res/network/ddos-protection-plan/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/dns-forwarding-ruleset/README.md b/avm/res/network/dns-forwarding-ruleset/README.md index 74096309a7..48d02ff5a2 100644 --- a/avm/res/network/dns-forwarding-ruleset/README.md +++ b/avm/res/network/dns-forwarding-ruleset/README.md @@ -128,11 +128,13 @@ module dnsForwardingRuleset 'br/public:avm/res/network/dns-forwarding-ruleset:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -207,11 +209,13 @@ module dnsForwardingRuleset 'br/public:avm/res/network/dns-forwarding-ruleset:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -518,6 +522,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -568,6 +573,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-forwarding-ruleset/forwarding-rule/main.json b/avm/res/network/dns-forwarding-ruleset/forwarding-rule/main.json index a78e018085..7946899042 100644 --- a/avm/res/network/dns-forwarding-ruleset/forwarding-rule/main.json +++ b/avm/res/network/dns-forwarding-ruleset/forwarding-rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11159744121670173662" + "version": "0.29.47.4906", + "templateHash": "17239011184377594139" }, "name": "Dns Forwarding Rulesets Forwarding Rules", "description": "This template deploys Forwarding Rule in a Dns Forwarding Ruleset.", diff --git a/avm/res/network/dns-forwarding-ruleset/main.bicep b/avm/res/network/dns-forwarding-ruleset/main.bicep index d18ce80c75..365e81861c 100644 --- a/avm/res/network/dns-forwarding-ruleset/main.bicep +++ b/avm/res/network/dns-forwarding-ruleset/main.bicep @@ -56,6 +56,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-dnsforwardingruleset.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -125,14 +136,14 @@ resource dnsForwardingRuleset_lock 'Microsoft.Authorization/locks@2020-05-01' = } resource dnsForwardingRuleset_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dnsForwardingRuleset.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + dnsForwardingRuleset.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -161,6 +172,9 @@ output location string = dnsForwardingRuleset.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-forwarding-ruleset/main.json b/avm/res/network/dns-forwarding-ruleset/main.json index 9eddce818e..be9b89f6d8 100644 --- a/avm/res/network/dns-forwarding-ruleset/main.json +++ b/avm/res/network/dns-forwarding-ruleset/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15934040549427781903" + "version": "0.29.47.4906", + "templateHash": "16210853783153815053" }, "name": "Dns Forwarding Rulesets", "description": "This template deploys an dns forwarding ruleset.", @@ -18,6 +18,13 @@ "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": { @@ -254,6 +261,13 @@ } }, "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')]", @@ -321,20 +335,20 @@ "dnsForwardingRuleset_roleAssignments": { "copy": { "name": "dnsForwardingRuleset_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsForwardingRulesets/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsForwardingRulesets', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsForwardingRulesets', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dnsForwardingRuleset" @@ -380,8 +394,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14759821855107391948" + "version": "0.29.47.4906", + "templateHash": "17239011184377594139" }, "name": "Dns Forwarding Rulesets Forwarding Rules", "description": "This template deploys Forwarding Rule in a Dns Forwarding Ruleset.", @@ -508,8 +522,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2413501505308144862" + "version": "0.29.47.4906", + "templateHash": "14775682641579876025" }, "name": "Dns Forwarding Rulesets Virtual Network Links", "description": "This template deploys Virtual Network Link in a Dns Forwarding Ruleset.", diff --git a/avm/res/network/dns-forwarding-ruleset/tests/e2e/max/main.test.bicep b/avm/res/network/dns-forwarding-ruleset/tests/e2e/max/main.test.bicep index 0f790b69ee..8a956dbc16 100644 --- a/avm/res/network/dns-forwarding-ruleset/tests/e2e/max/main.test.bicep +++ b/avm/res/network/dns-forwarding-ruleset/tests/e2e/max/main.test.bicep @@ -78,11 +78,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '38837eb6-838b-4c77-8d7d-baa102195d9f' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/dns-forwarding-ruleset/version.json b/avm/res/network/dns-forwarding-ruleset/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/dns-forwarding-ruleset/version.json +++ b/avm/res/network/dns-forwarding-ruleset/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/dns-forwarding-ruleset/virtual-network-link/main.json b/avm/res/network/dns-forwarding-ruleset/virtual-network-link/main.json index caaefb759a..3f0af9c907 100644 --- a/avm/res/network/dns-forwarding-ruleset/virtual-network-link/main.json +++ b/avm/res/network/dns-forwarding-ruleset/virtual-network-link/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "15095816376592886095" + "version": "0.29.47.4906", + "templateHash": "14775682641579876025" }, "name": "Dns Forwarding Rulesets Virtual Network Links", "description": "This template deploys Virtual Network Link in a Dns Forwarding Ruleset.", diff --git a/avm/res/network/dns-resolver/README.md b/avm/res/network/dns-resolver/README.md index eed91bd4a8..5c87ad489c 100644 --- a/avm/res/network/dns-resolver/README.md +++ b/avm/res/network/dns-resolver/README.md @@ -121,11 +121,13 @@ module dnsResolver 'br/public:avm/res/network/dns-resolver:' = { ] roleAssignments: [ { + name: '83c82ade-1ada-4374-82d0-325f39a44af6' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -193,11 +195,13 @@ module dnsResolver 'br/public:avm/res/network/dns-resolver:' = { "roleAssignments": { "value": [ { + "name": "83c82ade-1ada-4374-82d0-325f39a44af6", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -556,6 +560,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -606,6 +611,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-resolver/inbound-endpoint/main.json b/avm/res/network/dns-resolver/inbound-endpoint/main.json index 8830c57a9c..9a658a36a5 100644 --- a/avm/res/network/dns-resolver/inbound-endpoint/main.json +++ b/avm/res/network/dns-resolver/inbound-endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13768999645959207906" + "version": "0.29.47.4906", + "templateHash": "10392660876237354468" }, "name": "DNS Resolver Inbound Endpoint", "description": "This module deploys a DNS Resolver Inbound Endpoint.", diff --git a/avm/res/network/dns-resolver/main.bicep b/avm/res/network/dns-resolver/main.bicep index 4c8bc40727..e538a5975f 100644 --- a/avm/res/network/dns-resolver/main.bicep +++ b/avm/res/network/dns-resolver/main.bicep @@ -64,6 +64,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-dnsresolver.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -106,14 +117,10 @@ resource dnsResolver_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empt } resource dnsResolver_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dnsResolver.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(dnsResolver.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -170,6 +177,9 @@ output location string = dnsResolver.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-resolver/main.json b/avm/res/network/dns-resolver/main.json index 09296e57b9..3922380b24 100644 --- a/avm/res/network/dns-resolver/main.json +++ b/avm/res/network/dns-resolver/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6673442612196643096" + "version": "0.29.47.4906", + "templateHash": "8714442114797304062" }, "name": "DNS Resolver", "description": "This module deploys a DNS Resolver.", @@ -18,6 +18,13 @@ "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": { @@ -254,6 +261,13 @@ } }, "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')]", @@ -317,20 +331,20 @@ "dnsResolver_roleAssignments": { "copy": { "name": "dnsResolver_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsResolvers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsResolvers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsResolvers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dnsResolver" @@ -379,8 +393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7324585537468677431" + "version": "0.29.47.4906", + "templateHash": "10392660876237354468" }, "name": "DNS Resolver Inbound Endpoint", "description": "This module deploys a DNS Resolver Inbound Endpoint.", @@ -530,8 +544,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14584980693464566365" + "version": "0.29.47.4906", + "templateHash": "5025100485259764817" }, "name": "DNS Resolver Outbound Endpoint", "description": "This module deploys a DNS Resolver Outbound Endpoint.", diff --git a/avm/res/network/dns-resolver/outbound-endpoint/main.json b/avm/res/network/dns-resolver/outbound-endpoint/main.json index 2db11de3ef..b3417d056e 100644 --- a/avm/res/network/dns-resolver/outbound-endpoint/main.json +++ b/avm/res/network/dns-resolver/outbound-endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "6638776759595697100" + "version": "0.29.47.4906", + "templateHash": "5025100485259764817" }, "name": "DNS Resolver Outbound Endpoint", "description": "This module deploys a DNS Resolver Outbound Endpoint.", diff --git a/avm/res/network/dns-resolver/tests/e2e/max/main.test.bicep b/avm/res/network/dns-resolver/tests/e2e/max/main.test.bicep index b88dfaa63b..5f40c2af37 100644 --- a/avm/res/network/dns-resolver/tests/e2e/max/main.test.bicep +++ b/avm/res/network/dns-resolver/tests/e2e/max/main.test.bicep @@ -60,11 +60,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '83c82ade-1ada-4374-82d0-325f39a44af6' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/dns-resolver/version.json b/avm/res/network/dns-resolver/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/dns-resolver/version.json +++ b/avm/res/network/dns-resolver/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/dns-zone/README.md b/avm/res/network/dns-zone/README.md index 1519815413..816f090fa1 100644 --- a/avm/res/network/dns-zone/README.md +++ b/avm/res/network/dns-zone/README.md @@ -296,11 +296,13 @@ module dnsZone 'br/public:avm/res/network/dns-zone:' = { ] roleAssignments: [ { + name: 'a8697438-70e8-4f40-baa4-6e90a57fe1dc' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -640,11 +642,13 @@ module dnsZone 'br/public:avm/res/network/dns-zone:' = { "roleAssignments": { "value": [ { + "name": "a8697438-70e8-4f40-baa4-6e90a57fe1dc", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -954,6 +958,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-aroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-aroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-aroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-aroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-aroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `a.roleAssignments.principalId` @@ -1004,6 +1009,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `a.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `a.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1114,6 +1126,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-aaaaroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-aaaaroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-aaaaroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-aaaaroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-aaaaroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `aaaa.roleAssignments.principalId` @@ -1164,6 +1177,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `aaaa.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `aaaa.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1289,6 +1309,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-caaroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-caaroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-caaroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-caaroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-caaroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `caa.roleAssignments.principalId` @@ -1339,6 +1360,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `caa.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `caa.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1442,6 +1470,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-cnameroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-cnameroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-cnameroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-cnameroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-cnameroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `cname.roleAssignments.principalId` @@ -1492,6 +1521,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `cname.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `cname.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1661,6 +1697,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-mxroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-mxroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-mxroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-mxroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-mxroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `mx.roleAssignments.principalId` @@ -1711,6 +1748,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `mx.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `mx.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1813,6 +1857,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-nsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-nsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-nsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-nsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-nsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `ns.roleAssignments.principalId` @@ -1863,6 +1908,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `ns.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `ns.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1965,6 +2017,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-ptrroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-ptrroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-ptrroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-ptrroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-ptrroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `ptr.roleAssignments.principalId` @@ -2015,6 +2068,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `ptr.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `ptr.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2061,6 +2121,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2111,6 +2172,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2186,6 +2254,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-soaroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-soaroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-soaroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-soaroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-soaroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `soa.roleAssignments.principalId` @@ -2236,6 +2305,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `soa.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `soa.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2386,6 +2462,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-srvroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-srvroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-srvroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-srvroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-srvroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `srv.roleAssignments.principalId` @@ -2436,6 +2513,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `srv.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `srv.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2569,6 +2653,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-txtroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-txtroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-txtroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-txtroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-txtroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `txt.roleAssignments.principalId` @@ -2619,6 +2704,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `txt.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `txt.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2670,6 +2762,7 @@ The text value of this TXT record. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the DNS zone. | +| `nameServers` | array | The name servers of the DNS zone. | | `resourceGroupName` | string | The resource group the DNS zone was deployed into. | | `resourceId` | string | The resource ID of the DNS zone. | diff --git a/avm/res/network/dns-zone/a/README.md b/avm/res/network/dns-zone/a/README.md index 9c4092fdca..c87ee20a86 100644 --- a/avm/res/network/dns-zone/a/README.md +++ b/avm/res/network/dns-zone/a/README.md @@ -91,6 +91,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -141,6 +142,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/a/main.bicep b/avm/res/network/dns-zone/a/main.bicep index adf3374b2c..8f9d60922f 100644 --- a/avm/res/network/dns-zone/a/main.bicep +++ b/avm/res/network/dns-zone/a/main.bicep @@ -60,6 +60,17 @@ var builtInRoleNames = { '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)) + }) +] resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -80,14 +91,10 @@ resource A 'Microsoft.Network/dnsZones/A@2018-05-01' = { } resource A_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(A.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(A.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -112,6 +119,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/a/main.json b/avm/res/network/dns-zone/a/main.json index fbeeb5b1b1..df0f6b644a 100644 --- a/avm/res/network/dns-zone/a/main.json +++ b/avm/res/network/dns-zone/a/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10382086266285650461" + "version": "0.29.47.4906", + "templateHash": "757998912186666805" }, "name": "Public DNS Zone A record", "description": "This module deploys a Public DNS Zone A record.", @@ -18,6 +18,13 @@ "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": { @@ -129,6 +136,13 @@ } }, "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')]", @@ -167,20 +181,20 @@ "A_roleAssignments": { "copy": { "name": "A_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/A/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/A', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/A', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/aaaa/README.md b/avm/res/network/dns-zone/aaaa/README.md index 671a81392d..fe6beb6908 100644 --- a/avm/res/network/dns-zone/aaaa/README.md +++ b/avm/res/network/dns-zone/aaaa/README.md @@ -91,6 +91,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -141,6 +142,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/aaaa/main.bicep b/avm/res/network/dns-zone/aaaa/main.bicep index 505ff9e1e3..0c08a1f834 100644 --- a/avm/res/network/dns-zone/aaaa/main.bicep +++ b/avm/res/network/dns-zone/aaaa/main.bicep @@ -61,6 +61,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -81,14 +92,10 @@ resource AAAA 'Microsoft.Network/dnsZones/AAAA@2018-05-01' = { } resource AAAA_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(AAAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(AAAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -113,6 +120,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/aaaa/main.json b/avm/res/network/dns-zone/aaaa/main.json index 1bb5f82cc3..7192d58558 100644 --- a/avm/res/network/dns-zone/aaaa/main.json +++ b/avm/res/network/dns-zone/aaaa/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18415645623751805605" + "version": "0.29.47.4906", + "templateHash": "7014742305851451531" }, "name": "Public DNS Zone AAAA record", "description": "This module deploys a Public DNS Zone AAAA record.", @@ -18,6 +18,13 @@ "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": { @@ -129,6 +136,13 @@ } }, "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')]", @@ -167,20 +181,20 @@ "AAAA_roleAssignments": { "copy": { "name": "AAAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/AAAA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/AAAA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/AAAA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/caa/README.md b/avm/res/network/dns-zone/caa/README.md index 8e32a19d4f..db0e08c308 100644 --- a/avm/res/network/dns-zone/caa/README.md +++ b/avm/res/network/dns-zone/caa/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/caa/main.bicep b/avm/res/network/dns-zone/caa/main.bicep index 4f819bdbdf..4e05e82730 100644 --- a/avm/res/network/dns-zone/caa/main.bicep +++ b/avm/res/network/dns-zone/caa/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -73,14 +84,10 @@ resource CAA 'Microsoft.Network/dnsZones/CAA@2018-05-01' = { } resource CAA_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(CAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(CAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -105,6 +112,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/caa/main.json b/avm/res/network/dns-zone/caa/main.json index 5e54781fb6..ee588f2740 100644 --- a/avm/res/network/dns-zone/caa/main.json +++ b/avm/res/network/dns-zone/caa/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "301276815694552809" + "version": "0.29.47.4906", + "templateHash": "15336680989597404697" }, "name": "Public DNS Zone CAA record", "description": "This module deploys a Public DNS Zone CAA record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "CAA_roleAssignments": { "copy": { "name": "CAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/CAA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/CAA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/CAA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "CAA" diff --git a/avm/res/network/dns-zone/cname/README.md b/avm/res/network/dns-zone/cname/README.md index 546e901881..c9e5d99714 100644 --- a/avm/res/network/dns-zone/cname/README.md +++ b/avm/res/network/dns-zone/cname/README.md @@ -91,6 +91,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -141,6 +142,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/cname/main.bicep b/avm/res/network/dns-zone/cname/main.bicep index a1f17a1b01..b5373b54f6 100644 --- a/avm/res/network/dns-zone/cname/main.bicep +++ b/avm/res/network/dns-zone/cname/main.bicep @@ -61,6 +61,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -81,14 +92,10 @@ resource CNAME 'Microsoft.Network/dnsZones/CNAME@2018-05-01' = { } resource CNAME_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(CNAME.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(CNAME.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -113,6 +120,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/cname/main.json b/avm/res/network/dns-zone/cname/main.json index d26863cd6d..8025a23f35 100644 --- a/avm/res/network/dns-zone/cname/main.json +++ b/avm/res/network/dns-zone/cname/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17581694980048659433" + "version": "0.29.47.4906", + "templateHash": "13074182414562688422" }, "name": "Public DNS Zone CNAME record", "description": "This module deploys a Public DNS Zone CNAME record.", @@ -18,6 +18,13 @@ "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": { @@ -129,6 +136,13 @@ } }, "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')]", @@ -167,20 +181,20 @@ "CNAME_roleAssignments": { "copy": { "name": "CNAME_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/CNAME/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/CNAME', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/CNAME', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/main.bicep b/avm/res/network/dns-zone/main.bicep index b3956db67f..85c81b2d53 100644 --- a/avm/res/network/dns-zone/main.bicep +++ b/avm/res/network/dns-zone/main.bicep @@ -90,6 +90,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-dnszone.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -273,14 +284,10 @@ resource dnsZone_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource dnsZone_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dnsZone.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(dnsZone.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -304,6 +311,9 @@ output resourceId string = dnsZone.id @description('The location the resource was deployed into.') output location string = dnsZone.location +@description('The name servers of the DNS zone.') +output nameServers array = dnsZone.properties.nameServers + // =============== // // Definitions // // =============== // @@ -317,6 +327,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/main.json b/avm/res/network/dns-zone/main.json index d5a674f4c4..fbda4b028e 100644 --- a/avm/res/network/dns-zone/main.json +++ b/avm/res/network/dns-zone/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5527907345622419255" + "version": "0.29.47.4906", + "templateHash": "2883962289867556365" }, "name": "Public DNS Zones", "description": "This module deploys a Public DNS zone.", @@ -43,6 +43,13 @@ "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": { @@ -831,6 +838,13 @@ } }, "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')]", @@ -893,20 +907,20 @@ "dnsZone_roleAssignments": { "copy": { "name": "dnsZone_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dnsZone" @@ -955,8 +969,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "603896374638063243" + "version": "0.29.47.4906", + "templateHash": "757998912186666805" }, "name": "Public DNS Zone A record", "description": "This module deploys a Public DNS Zone A record.", @@ -968,6 +982,13 @@ "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": { @@ -1079,6 +1100,13 @@ } }, "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')]", @@ -1117,20 +1145,20 @@ "A_roleAssignments": { "copy": { "name": "A_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/A/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/A', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/A', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1209,8 +1237,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "253132503930531398" + "version": "0.29.47.4906", + "templateHash": "7014742305851451531" }, "name": "Public DNS Zone AAAA record", "description": "This module deploys a Public DNS Zone AAAA record.", @@ -1222,6 +1250,13 @@ "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": { @@ -1333,6 +1368,13 @@ } }, "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')]", @@ -1371,20 +1413,20 @@ "AAAA_roleAssignments": { "copy": { "name": "AAAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/AAAA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/AAAA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/AAAA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1463,8 +1505,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4160326823053069155" + "version": "0.29.47.4906", + "templateHash": "13074182414562688422" }, "name": "Public DNS Zone CNAME record", "description": "This module deploys a Public DNS Zone CNAME record.", @@ -1476,6 +1518,13 @@ "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": { @@ -1587,6 +1636,13 @@ } }, "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')]", @@ -1625,20 +1681,20 @@ "CNAME_roleAssignments": { "copy": { "name": "CNAME_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/CNAME/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/CNAME', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/CNAME', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1714,8 +1770,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17450934468569447558" + "version": "0.29.47.4906", + "templateHash": "15336680989597404697" }, "name": "Public DNS Zone CAA record", "description": "This module deploys a Public DNS Zone CAA record.", @@ -1727,6 +1783,13 @@ "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": { @@ -1831,6 +1894,13 @@ } }, "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')]", @@ -1868,20 +1938,20 @@ "CAA_roleAssignments": { "copy": { "name": "CAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/CAA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/CAA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/CAA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "CAA" @@ -1957,8 +2027,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18360536631272316746" + "version": "0.29.47.4906", + "templateHash": "5108823161751025831" }, "name": "Public DNS Zone MX record", "description": "This module deploys a Public DNS Zone MX record.", @@ -1970,6 +2040,13 @@ "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": { @@ -2074,6 +2151,13 @@ } }, "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')]", @@ -2111,20 +2195,20 @@ "MX_roleAssignments": { "copy": { "name": "MX_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/MX/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/MX', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/MX', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2200,8 +2284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2515009703809285257" + "version": "0.29.47.4906", + "templateHash": "11663723082194219520" }, "name": "Public DNS Zone NS record", "description": "This module deploys a Public DNS Zone NS record.", @@ -2213,6 +2297,13 @@ "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": { @@ -2317,6 +2408,13 @@ } }, "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')]", @@ -2354,20 +2452,20 @@ "NS_roleAssignments": { "copy": { "name": "NS_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/NS/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/NS', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/NS', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "NS" @@ -2443,8 +2541,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17542593500975270371" + "version": "0.29.47.4906", + "templateHash": "9140925940202553676" }, "name": "Public DNS Zone PTR record", "description": "This module deploys a Public DNS Zone PTR record.", @@ -2456,6 +2554,13 @@ "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": { @@ -2560,6 +2665,13 @@ } }, "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')]", @@ -2597,20 +2709,20 @@ "PTR_roleAssignments": { "copy": { "name": "PTR_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/PTR/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/PTR', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/PTR', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2686,8 +2798,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16189776704337814971" + "version": "0.29.47.4906", + "templateHash": "14564615037136781676" }, "name": "Public DNS Zone SOA record", "description": "This module deploys a Public DNS Zone SOA record.", @@ -2699,6 +2811,13 @@ "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": { @@ -2803,6 +2922,13 @@ } }, "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')]", @@ -2840,20 +2966,20 @@ "SOA_roleAssignments": { "copy": { "name": "SOA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/SOA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/SOA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/SOA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2929,8 +3055,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12794443790243830757" + "version": "0.29.47.4906", + "templateHash": "4581413191057367399" }, "name": "Public DNS Zone SRV record", "description": "This module deploys a Public DNS Zone SRV record.", @@ -2942,6 +3068,13 @@ "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": { @@ -3046,6 +3179,13 @@ } }, "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')]", @@ -3083,20 +3223,20 @@ "SRV_roleAssignments": { "copy": { "name": "SRV_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/SRV/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/SRV', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/SRV', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -3172,8 +3312,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "497559138132140251" + "version": "0.29.47.4906", + "templateHash": "5111536727799165587" }, "name": "Public DNS Zone TXT record", "description": "This module deploys a Public DNS Zone TXT record.", @@ -3185,6 +3325,13 @@ "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": { @@ -3289,6 +3436,13 @@ } }, "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')]", @@ -3326,20 +3480,20 @@ "TXT_roleAssignments": { "copy": { "name": "TXT_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/TXT/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/TXT', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/TXT', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -3404,6 +3558,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('dnsZone', '2018-05-01', 'full').location]" + }, + "nameServers": { + "type": "array", + "metadata": { + "description": "The name servers of the DNS zone." + }, + "value": "[reference('dnsZone').nameServers]" } } } \ No newline at end of file diff --git a/avm/res/network/dns-zone/mx/README.md b/avm/res/network/dns-zone/mx/README.md index f9b8e22a97..231ff240e1 100644 --- a/avm/res/network/dns-zone/mx/README.md +++ b/avm/res/network/dns-zone/mx/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/mx/main.bicep b/avm/res/network/dns-zone/mx/main.bicep index 758fd767d1..48d4e48c1f 100644 --- a/avm/res/network/dns-zone/mx/main.bicep +++ b/avm/res/network/dns-zone/mx/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -73,14 +84,10 @@ resource MX 'Microsoft.Network/dnsZones/MX@2018-05-01' = { } resource MX_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(MX.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(MX.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -105,6 +112,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/mx/main.json b/avm/res/network/dns-zone/mx/main.json index 837eb9f6e0..da370db49a 100644 --- a/avm/res/network/dns-zone/mx/main.json +++ b/avm/res/network/dns-zone/mx/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "3841788058898566148" + "version": "0.29.47.4906", + "templateHash": "5108823161751025831" }, "name": "Public DNS Zone MX record", "description": "This module deploys a Public DNS Zone MX record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "MX_roleAssignments": { "copy": { "name": "MX_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/MX/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/MX', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/MX', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/ns/README.md b/avm/res/network/dns-zone/ns/README.md index 0799543cd2..8982e4f4a0 100644 --- a/avm/res/network/dns-zone/ns/README.md +++ b/avm/res/network/dns-zone/ns/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/ns/main.bicep b/avm/res/network/dns-zone/ns/main.bicep index 3e4cbe4f40..2f1839197f 100644 --- a/avm/res/network/dns-zone/ns/main.bicep +++ b/avm/res/network/dns-zone/ns/main.bicep @@ -57,6 +57,17 @@ var builtInRoleNames = { '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)) + }) +] resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -72,14 +83,10 @@ resource NS 'Microsoft.Network/dnsZones/NS@2018-05-01' = { } resource NS_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(NS.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(NS.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -104,6 +111,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/ns/main.json b/avm/res/network/dns-zone/ns/main.json index 7a5cbc9f1c..94c90714f8 100644 --- a/avm/res/network/dns-zone/ns/main.json +++ b/avm/res/network/dns-zone/ns/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10425578150391717396" + "version": "0.29.47.4906", + "templateHash": "11663723082194219520" }, "name": "Public DNS Zone NS record", "description": "This module deploys a Public DNS Zone NS record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "NS_roleAssignments": { "copy": { "name": "NS_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/NS/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/NS', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/NS', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "NS" diff --git a/avm/res/network/dns-zone/ptr/README.md b/avm/res/network/dns-zone/ptr/README.md index 440d522b3e..acf3711a10 100644 --- a/avm/res/network/dns-zone/ptr/README.md +++ b/avm/res/network/dns-zone/ptr/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/ptr/main.bicep b/avm/res/network/dns-zone/ptr/main.bicep index a3463a2ce3..cb82fbcf1f 100644 --- a/avm/res/network/dns-zone/ptr/main.bicep +++ b/avm/res/network/dns-zone/ptr/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -73,14 +84,10 @@ resource PTR 'Microsoft.Network/dnsZones/PTR@2018-05-01' = { } resource PTR_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(PTR.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(PTR.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -105,6 +112,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/ptr/main.json b/avm/res/network/dns-zone/ptr/main.json index 19229a1fef..34bbd57230 100644 --- a/avm/res/network/dns-zone/ptr/main.json +++ b/avm/res/network/dns-zone/ptr/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12898716138667066374" + "version": "0.29.47.4906", + "templateHash": "9140925940202553676" }, "name": "Public DNS Zone PTR record", "description": "This module deploys a Public DNS Zone PTR record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "PTR_roleAssignments": { "copy": { "name": "PTR_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/PTR/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/PTR', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/PTR', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/soa/README.md b/avm/res/network/dns-zone/soa/README.md index a85930e055..efa9ecc325 100644 --- a/avm/res/network/dns-zone/soa/README.md +++ b/avm/res/network/dns-zone/soa/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/soa/main.bicep b/avm/res/network/dns-zone/soa/main.bicep index 17eceec692..8596cbb21b 100644 --- a/avm/res/network/dns-zone/soa/main.bicep +++ b/avm/res/network/dns-zone/soa/main.bicep @@ -58,6 +58,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -73,14 +84,10 @@ resource SOA 'Microsoft.Network/dnsZones/SOA@2018-05-01' = { } resource SOA_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(SOA.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(SOA.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -105,6 +112,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/soa/main.json b/avm/res/network/dns-zone/soa/main.json index 0aa645e575..e106c03424 100644 --- a/avm/res/network/dns-zone/soa/main.json +++ b/avm/res/network/dns-zone/soa/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18001570877412823352" + "version": "0.29.47.4906", + "templateHash": "14564615037136781676" }, "name": "Public DNS Zone SOA record", "description": "This module deploys a Public DNS Zone SOA record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "SOA_roleAssignments": { "copy": { "name": "SOA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/SOA/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/SOA', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/SOA', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/srv/README.md b/avm/res/network/dns-zone/srv/README.md index 62e041bd70..029dc6761d 100644 --- a/avm/res/network/dns-zone/srv/README.md +++ b/avm/res/network/dns-zone/srv/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/srv/main.bicep b/avm/res/network/dns-zone/srv/main.bicep index 4250514384..52c9ac62e2 100644 --- a/avm/res/network/dns-zone/srv/main.bicep +++ b/avm/res/network/dns-zone/srv/main.bicep @@ -57,6 +57,17 @@ var builtInRoleNames = { '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)) + }) +] resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -72,14 +83,10 @@ resource SRV 'Microsoft.Network/dnsZones/SRV@2018-05-01' = { } resource SRV_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(SRV.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(SRV.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -104,6 +111,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/srv/main.json b/avm/res/network/dns-zone/srv/main.json index 14b4022c2d..e8e21b9958 100644 --- a/avm/res/network/dns-zone/srv/main.json +++ b/avm/res/network/dns-zone/srv/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11050875313013276495" + "version": "0.29.47.4906", + "templateHash": "4581413191057367399" }, "name": "Public DNS Zone SRV record", "description": "This module deploys a Public DNS Zone SRV record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "SRV_roleAssignments": { "copy": { "name": "SRV_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/SRV/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/SRV', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/SRV', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/tests/e2e/max/main.test.bicep b/avm/res/network/dns-zone/tests/e2e/max/main.test.bicep index 0b531eccc5..2e88f60af9 100644 --- a/avm/res/network/dns-zone/tests/e2e/max/main.test.bicep +++ b/avm/res/network/dns-zone/tests/e2e/max/main.test.bicep @@ -197,11 +197,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'a8697438-70e8-4f40-baa4-6e90a57fe1dc' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/dns-zone/txt/README.md b/avm/res/network/dns-zone/txt/README.md index be34501228..010487f1bd 100644 --- a/avm/res/network/dns-zone/txt/README.md +++ b/avm/res/network/dns-zone/txt/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/dns-zone/txt/main.bicep b/avm/res/network/dns-zone/txt/main.bicep index 2477ee357e..4be83d7ce6 100644 --- a/avm/res/network/dns-zone/txt/main.bicep +++ b/avm/res/network/dns-zone/txt/main.bicep @@ -57,6 +57,17 @@ var builtInRoleNames = { '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)) + }) +] resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' existing = { name: dnsZoneName } @@ -72,14 +83,10 @@ resource TXT 'Microsoft.Network/dnsZones/TXT@2018-05-01' = { } resource TXT_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(TXT.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(TXT.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -104,6 +111,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/dns-zone/txt/main.json b/avm/res/network/dns-zone/txt/main.json index 880784f9ac..d7cd9dfab3 100644 --- a/avm/res/network/dns-zone/txt/main.json +++ b/avm/res/network/dns-zone/txt/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10316996934699244293" + "version": "0.29.47.4906", + "templateHash": "5111536727799165587" }, "name": "Public DNS Zone TXT record", "description": "This module deploys a Public DNS Zone TXT record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -159,20 +173,20 @@ "TXT_roleAssignments": { "copy": { "name": "TXT_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/dnsZones/{0}/TXT/{1}', parameters('dnsZoneName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/dnsZones/TXT', parameters('dnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/dnsZones/TXT', parameters('dnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/dns-zone/version.json b/avm/res/network/dns-zone/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/dns-zone/version.json +++ b/avm/res/network/dns-zone/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/express-route-circuit/README.md b/avm/res/network/express-route-circuit/README.md index 2294f5b3f4..f0c9b038ce 100644 --- a/avm/res/network/express-route-circuit/README.md +++ b/avm/res/network/express-route-circuit/README.md @@ -133,11 +133,13 @@ module expressRouteCircuit 'br/public:avm/res/network/express-route-circuit:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -216,11 +218,13 @@ module expressRouteCircuit 'br/public:avm/res/network/express-route-circuit:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -731,6 +735,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -781,6 +786,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/express-route-circuit/main.bicep b/avm/res/network/express-route-circuit/main.bicep index 82f4784532..26e0599131 100644 --- a/avm/res/network/express-route-circuit/main.bicep +++ b/avm/res/network/express-route-circuit/main.bicep @@ -103,6 +103,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-expressroutecircuit.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -122,7 +133,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2023-04-01' = { +resource expressRouteCircuit 'Microsoft.Network/expressRouteCircuits@2023-04-01' = { name: name location: location tags: tags @@ -163,7 +174,7 @@ resource expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2023-04-01 } } -resource expressRouteCircuits_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { +resource expressRouteCircuit_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { name: lock.?name ?? 'lock-${name}' properties: { level: lock.?kind ?? '' @@ -171,10 +182,10 @@ resource expressRouteCircuits_lock 'Microsoft.Authorization/locks@2020-05-01' = ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' } - scope: expressRouteCircuits + scope: expressRouteCircuit } -resource expressRouteCircuits_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ +resource expressRouteCircuit_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ for (diagnosticSetting, index) in (diagnosticSettings ?? []): { name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' properties: { @@ -199,19 +210,19 @@ resource expressRouteCircuits_diagnosticSettings 'Microsoft.Insights/diagnosticS marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType } - scope: expressRouteCircuits + scope: expressRouteCircuit } ] -resource expressRouteCircuits_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(expressRouteCircuits.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) +resource expressRouteCircuit_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + expressRouteCircuit.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -219,24 +230,24 @@ resource expressRouteCircuits_roleAssignments 'Microsoft.Authorization/roleAssig conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId } - scope: expressRouteCircuits + scope: expressRouteCircuit } ] @description('The resource ID of express route curcuit.') -output resourceId string = expressRouteCircuits.id +output resourceId string = expressRouteCircuit.id @description('The resource group the express route curcuit was deployed into.') output resourceGroupName string = resourceGroup().name @description('The name of express route curcuit.') -output name string = expressRouteCircuits.name +output name string = expressRouteCircuit.name @description('The service key of the express route circuit.') -output serviceKey string = expressRouteCircuits.properties.serviceKey +output serviceKey string = expressRouteCircuit.properties.serviceKey @description('The location the resource was deployed into.') -output location string = expressRouteCircuits.location +output location string = expressRouteCircuit.location // =============== // // Definitions // @@ -251,6 +262,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/express-route-circuit/main.json b/avm/res/network/express-route-circuit/main.json index 7c2e264ddb..f89ea8c582 100644 --- a/avm/res/network/express-route-circuit/main.json +++ b/avm/res/network/express-route-circuit/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15879717476286891865" + "version": "0.29.47.4906", + "templateHash": "8576129412058428008" }, "name": "ExpressRoute Circuits", "description": "This module deploys an Express Route Circuit.", @@ -43,6 +43,13 @@ "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": { @@ -395,6 +402,13 @@ } }, "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')]", @@ -425,7 +439,7 @@ } } }, - "expressRouteCircuits": { + "expressRouteCircuit": { "type": "Microsoft.Network/expressRouteCircuits", "apiVersion": "2023-04-01", "name": "[parameters('name')]", @@ -449,7 +463,7 @@ "peerings": "[if(parameters('peering'), createArray(createObject('name', parameters('peeringType'), 'properties', createObject('peeringType', parameters('peeringType'), 'sharedKey', parameters('sharedKey'), 'peerASN', parameters('peerASN'), 'primaryPeerAddressPrefix', parameters('primaryPeerAddressPrefix'), 'secondaryPeerAddressPrefix', parameters('secondaryPeerAddressPrefix'), 'vlanId', parameters('vlanId')))), null())]" } }, - "expressRouteCircuits_lock": { + "expressRouteCircuit_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", @@ -460,12 +474,12 @@ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "expressRouteCircuits" + "expressRouteCircuit" ] }, - "expressRouteCircuits_diagnosticSettings": { + "expressRouteCircuit_diagnosticSettings": { "copy": { - "name": "expressRouteCircuits_diagnosticSettings", + "name": "expressRouteCircuit_diagnosticSettings", "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, "type": "Microsoft.Insights/diagnosticSettings", @@ -501,29 +515,29 @@ "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, "dependsOn": [ - "expressRouteCircuits" + "expressRouteCircuit" ] }, - "expressRouteCircuits_roleAssignments": { + "expressRouteCircuit_roleAssignments": { "copy": { - "name": "expressRouteCircuits_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "expressRouteCircuit_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/expressRouteCircuits/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/expressRouteCircuits', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/expressRouteCircuits', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ - "expressRouteCircuits" + "expressRouteCircuit" ] } }, @@ -554,14 +568,14 @@ "metadata": { "description": "The service key of the express route circuit." }, - "value": "[reference('expressRouteCircuits').serviceKey]" + "value": "[reference('expressRouteCircuit').serviceKey]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('expressRouteCircuits', '2023-04-01', 'full').location]" + "value": "[reference('expressRouteCircuit', '2023-04-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/network/express-route-circuit/tests/e2e/max/main.test.bicep b/avm/res/network/express-route-circuit/tests/e2e/max/main.test.bicep index 56e3591e91..4bc2ab2f3e 100644 --- a/avm/res/network/express-route-circuit/tests/e2e/max/main.test.bicep +++ b/avm/res/network/express-route-circuit/tests/e2e/max/main.test.bicep @@ -88,11 +88,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'd7aa3dfa-6ba6-4ed8-b561-2164fbb1327e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/express-route-circuit/version.json b/avm/res/network/express-route-circuit/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/network/express-route-circuit/version.json +++ b/avm/res/network/express-route-circuit/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/express-route-gateway/README.md b/avm/res/network/express-route-gateway/README.md index f626ed4024..6602edb4ca 100644 --- a/avm/res/network/express-route-gateway/README.md +++ b/avm/res/network/express-route-gateway/README.md @@ -109,11 +109,13 @@ module expressRouteGateway 'br/public:avm/res/network/express-route-gateway:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -170,11 +172,13 @@ module expressRouteGateway 'br/public:avm/res/network/express-route-gateway:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -423,6 +427,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -473,6 +478,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/express-route-gateway/main.bicep b/avm/res/network/express-route-gateway/main.bicep index 6e30c398ba..3690950f07 100644 --- a/avm/res/network/express-route-gateway/main.bicep +++ b/avm/res/network/express-route-gateway/main.bicep @@ -53,6 +53,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-expressroutegateway.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -103,14 +114,14 @@ resource expressRouteGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = i } resource expressRouteGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(expressRouteGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + expressRouteGateway.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -147,6 +158,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/express-route-gateway/main.json b/avm/res/network/express-route-gateway/main.json index 317ee69449..f8e8c3f584 100644 --- a/avm/res/network/express-route-gateway/main.json +++ b/avm/res/network/express-route-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "6587963349876161293" + "version": "0.29.47.4906", + "templateHash": "3952012926909383824" }, "name": "Express Route Gateways", "description": "This module deploys an Express Route Gateway.", @@ -43,6 +43,13 @@ "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": { @@ -181,6 +188,13 @@ } }, "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')]", @@ -248,20 +262,20 @@ "expressRouteGateway_roleAssignments": { "copy": { "name": "expressRouteGateway_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/expressRouteGateways/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/expressRouteGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/expressRouteGateways', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "expressRouteGateway" diff --git a/avm/res/network/express-route-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/express-route-gateway/tests/e2e/max/main.test.bicep index 110c5a0026..5a8c49a603 100644 --- a/avm/res/network/express-route-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/express-route-gateway/tests/e2e/max/main.test.bicep @@ -64,11 +64,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: '78ad6c3f-7f77-4d26-9576-dbd947241ef0' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/express-route-gateway/version.json b/avm/res/network/express-route-gateway/version.json index 04a0dd1a80..e42c3d9e5f 100644 --- a/avm/res/network/express-route-gateway/version.json +++ b/avm/res/network/express-route-gateway/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/front-door-web-application-firewall-policy/README.md b/avm/res/network/front-door-web-application-firewall-policy/README.md index 0efbac323a..cfa79fab15 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/README.md +++ b/avm/res/network/front-door-web-application-firewall-policy/README.md @@ -164,11 +164,13 @@ module frontDoorWebApplicationFirewallPolicy 'br/public:avm/res/network/front-do } roleAssignments: [ { + name: 'bb049c96-2571-4a25-b760-444ab25d86ed' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -286,11 +288,13 @@ module frontDoorWebApplicationFirewallPolicy 'br/public:avm/res/network/front-do "roleAssignments": { "value": [ { + "name": "bb049c96-2571-4a25-b760-444ab25d86ed", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -689,6 +693,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -739,6 +744,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/front-door-web-application-firewall-policy/main.bicep b/avm/res/network/front-door-web-application-firewall-policy/main.bicep index 254e584db5..de60fe5ffc 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/main.bicep +++ b/avm/res/network/front-door-web-application-firewall-policy/main.bicep @@ -89,9 +89,20 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { - name: '46d3xbcp.network-frontdoorwebappfirewallpolicy.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + name: '46d3xbcp.res.network-frontdoorwebappfwpolicy.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' properties: { mode: 'Incremental' template: { @@ -134,14 +145,14 @@ resource frontDoorWAFPolicy_lock 'Microsoft.Authorization/locks@2020-05-01' = if } resource frontDoorWAFPolicy_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(frontDoorWAFPolicy.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + frontDoorWAFPolicy.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -178,6 +189,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/front-door-web-application-firewall-policy/main.json b/avm/res/network/front-door-web-application-firewall-policy/main.json index 8fc4696575..52958c1ef6 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/main.json +++ b/avm/res/network/front-door-web-application-firewall-policy/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9012434638514265083" + "version": "0.29.47.4906", + "templateHash": "12441964439062961983" }, "name": "Front Door Web Application Firewall (WAF) Policies", "description": "This module deploys a Front Door Web Application Firewall (WAF) Policy.", @@ -43,6 +43,13 @@ "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": { @@ -220,6 +227,13 @@ } }, "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')]", @@ -233,7 +247,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.network-frontdoorwebappfirewallpolicy.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-frontdoorwebappfwpolicy.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -281,20 +295,20 @@ "frontDoorWAFPolicy_roleAssignments": { "copy": { "name": "frontDoorWAFPolicy_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/FrontDoorWebApplicationFirewallPolicies/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/FrontDoorWebApplicationFirewallPolicies', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/FrontDoorWebApplicationFirewallPolicies', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "frontDoorWAFPolicy" diff --git a/avm/res/network/front-door-web-application-firewall-policy/tests/e2e/max/main.test.bicep b/avm/res/network/front-door-web-application-firewall-policy/tests/e2e/max/main.test.bicep index d79784401c..e513fcbed3 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/tests/e2e/max/main.test.bicep +++ b/avm/res/network/front-door-web-application-firewall-policy/tests/e2e/max/main.test.bicep @@ -126,11 +126,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'bb049c96-2571-4a25-b760-444ab25d86ed' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/front-door-web-application-firewall-policy/version.json b/avm/res/network/front-door-web-application-firewall-policy/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/version.json +++ b/avm/res/network/front-door-web-application-firewall-policy/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/front-door/ORPHANED.md b/avm/res/network/front-door/ORPHANED.md new file mode 100644 index 0000000000..ef8fa911d2 --- /dev/null +++ b/avm/res/network/front-door/ORPHANED.md @@ -0,0 +1,4 @@ +⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ + +- Only security and bug fixes are being handled by the AVM core team at present. +- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/res/network/front-door/README.md b/avm/res/network/front-door/README.md index 06dd319027..a436357452 100644 --- a/avm/res/network/front-door/README.md +++ b/avm/res/network/front-door/README.md @@ -1,5 +1,10 @@ # Azure Front Doors `[Microsoft.Network/frontDoors]` +> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ +> +> - Only security and bug fixes are being handled by the AVM core team at present. +> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! + This module deploys an Azure Front Door. ## Navigation @@ -377,11 +382,13 @@ module frontDoor 'br/public:avm/res/network/front-door:' = { } roleAssignments: [ { + name: 'b2c1ef5f-3422-4a49-8e55-7789fe980b64' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -551,11 +558,13 @@ module frontDoor 'br/public:avm/res/network/front-door:' = { "roleAssignments": { "value": [ { + "name": "b2c1ef5f-3422-4a49-8e55-7789fe980b64", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1166,6 +1175,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1216,6 +1226,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/front-door/main.bicep b/avm/res/network/front-door/main.bicep index 113b0a556b..eef46273b3 100644 --- a/avm/res/network/front-door/main.bicep +++ b/avm/res/network/front-door/main.bicep @@ -71,6 +71,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-frontdoor.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -150,14 +161,10 @@ resource frontDoor_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 ] resource frontDoor_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(frontDoor.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(frontDoor.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -191,6 +198,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/front-door/main.json b/avm/res/network/front-door/main.json index a884ff582e..46ee05c293 100644 --- a/avm/res/network/front-door/main.json +++ b/avm/res/network/front-door/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9980065986350262429" + "version": "0.29.47.4906", + "templateHash": "13725022226932755148" }, "name": "Azure Front Doors", "description": "This module deploys an Azure Front Door.", @@ -43,6 +43,13 @@ "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": { @@ -334,6 +341,13 @@ } }, "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')]", @@ -442,20 +456,20 @@ "frontDoor_roleAssignments": { "copy": { "name": "frontDoor_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/frontDoors/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/frontDoors', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/frontDoors', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "frontDoor" diff --git a/avm/res/network/front-door/tests/e2e/max/main.test.bicep b/avm/res/network/front-door/tests/e2e/max/main.test.bicep index 4207e72e65..53381f05fc 100644 --- a/avm/res/network/front-door/tests/e2e/max/main.test.bicep +++ b/avm/res/network/front-door/tests/e2e/max/main.test.bicep @@ -160,11 +160,13 @@ module testDeployment '../../../main.bicep' = [ sendRecvTimeoutSeconds: 10 roleAssignments: [ { + name: 'b2c1ef5f-3422-4a49-8e55-7789fe980b64' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/front-door/version.json b/avm/res/network/front-door/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/network/front-door/version.json +++ b/avm/res/network/front-door/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/ip-group/README.md b/avm/res/network/ip-group/README.md index e1c6409b70..b50a7c025d 100644 --- a/avm/res/network/ip-group/README.md +++ b/avm/res/network/ip-group/README.md @@ -106,11 +106,13 @@ module ipGroup 'br/public:avm/res/network/ip-group:' = { } roleAssignments: [ { + name: '26438d40-c8be-4229-ba65-800cf4e49dc8' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -165,11 +167,13 @@ module ipGroup 'br/public:avm/res/network/ip-group:' = { "roleAssignments": { "value": [ { + "name": "26438d40-c8be-4229-ba65-800cf4e49dc8", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -374,6 +378,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -424,6 +429,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/ip-group/main.bicep b/avm/res/network/ip-group/main.bicep index 7345add07d..c35a17a7f6 100644 --- a/avm/res/network/ip-group/main.bicep +++ b/avm/res/network/ip-group/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-ipgroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -82,14 +93,10 @@ resource ipGroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource ipGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(ipGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(ipGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -126,6 +133,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/ip-group/main.json b/avm/res/network/ip-group/main.json index 5c00e0a7f6..5f8c6c7036 100644 --- a/avm/res/network/ip-group/main.json +++ b/avm/res/network/ip-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18446464970036456116" + "version": "0.29.47.4906", + "templateHash": "17963791752166007149" }, "name": "IP Groups", "description": "This module deploys an IP Group.", @@ -43,6 +43,13 @@ "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": { @@ -155,6 +162,13 @@ } }, "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')]", "IPAM Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b3e853f-ad5d-4fb5-a7b8-56a3581c7037')]", @@ -212,20 +226,20 @@ "ipGroup_roleAssignments": { "copy": { "name": "ipGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/ipGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/ipGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/ipGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "ipGroup" diff --git a/avm/res/network/ip-group/tests/e2e/max/main.test.bicep b/avm/res/network/ip-group/tests/e2e/max/main.test.bicep index 7b64b4b2be..fa0d52fb08 100644 --- a/avm/res/network/ip-group/tests/e2e/max/main.test.bicep +++ b/avm/res/network/ip-group/tests/e2e/max/main.test.bicep @@ -62,11 +62,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '26438d40-c8be-4229-ba65-800cf4e49dc8' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/ip-group/version.json b/avm/res/network/ip-group/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/network/ip-group/version.json +++ b/avm/res/network/ip-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/load-balancer/README.md b/avm/res/network/load-balancer/README.md index 3884d0a82c..5f671647b1 100644 --- a/avm/res/network/load-balancer/README.md +++ b/avm/res/network/load-balancer/README.md @@ -749,11 +749,13 @@ module loadBalancer 'br/public:avm/res/network/load-balancer:' = { ] roleAssignments: [ { + name: '3a5b2a4a-3584-4d6b-9cf0-ceb1e4f88a5d' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -915,11 +917,13 @@ module loadBalancer 'br/public:avm/res/network/load-balancer:' = { "roleAssignments": { "value": [ { + "name": "3a5b2a4a-3584-4d6b-9cf0-ceb1e4f88a5d", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1473,6 +1477,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1523,6 +1528,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/load-balancer/backend-address-pool/main.json b/avm/res/network/load-balancer/backend-address-pool/main.json index bd5560b6af..c91188cf10 100644 --- a/avm/res/network/load-balancer/backend-address-pool/main.json +++ b/avm/res/network/load-balancer/backend-address-pool/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1736731506120599540" + "version": "0.29.47.4906", + "templateHash": "3982666655179652966" }, "name": "Load Balancer Backend Address Pools", "description": "This module deploys a Load Balancer Backend Address Pools.", diff --git a/avm/res/network/load-balancer/inbound-nat-rule/main.json b/avm/res/network/load-balancer/inbound-nat-rule/main.json index 1982d3c54c..2e0551e452 100644 --- a/avm/res/network/load-balancer/inbound-nat-rule/main.json +++ b/avm/res/network/load-balancer/inbound-nat-rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11398827458130316747" + "version": "0.29.47.4906", + "templateHash": "6803574874056484597" }, "name": "Load Balancer Inbound NAT Rules", "description": "This module deploys a Load Balancer Inbound NAT Rules.", diff --git a/avm/res/network/load-balancer/main.bicep b/avm/res/network/load-balancer/main.bicep index 4d4ca72d08..4465f3843d 100644 --- a/avm/res/network/load-balancer/main.bicep +++ b/avm/res/network/load-balancer/main.bicep @@ -207,6 +207,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============ // // Dependencies // // ============ // @@ -329,14 +340,10 @@ resource loadBalancer_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@ ] resource loadBalancer_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(loadBalancer.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(loadBalancer.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -424,6 +431,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/load-balancer/main.json b/avm/res/network/load-balancer/main.json index fdca0df715..1c0a372143 100644 --- a/avm/res/network/load-balancer/main.json +++ b/avm/res/network/load-balancer/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7351695071160963851" + "version": "0.29.47.4906", + "templateHash": "16763526395876685201" }, "name": "Load Balancers", "description": "This module deploys a Load Balancer.", @@ -163,6 +163,13 @@ "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": { @@ -411,6 +418,11 @@ "input": { "name": "[coalesce(parameters('backendAddressPools'), createArray())[copyIndex('backendAddressPoolNames')].name]" } + }, + { + "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": { @@ -518,20 +530,20 @@ "loadBalancer_roleAssignments": { "copy": { "name": "loadBalancer_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/loadBalancers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/loadBalancers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/loadBalancers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "loadBalancer" @@ -567,8 +579,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1736731506120599540" + "version": "0.29.47.4906", + "templateHash": "3982666655179652966" }, "name": "Load Balancer Backend Address Pools", "description": "This module deploys a Load Balancer Backend Address Pools.", @@ -721,8 +733,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11398827458130316747" + "version": "0.29.47.4906", + "templateHash": "6803574874056484597" }, "name": "Load Balancer Inbound NAT Rules", "description": "This module deploys a Load Balancer Inbound NAT Rules.", diff --git a/avm/res/network/load-balancer/tests/e2e/max/main.test.bicep b/avm/res/network/load-balancer/tests/e2e/max/main.test.bicep index 69ab6344b2..c095cf9bcd 100644 --- a/avm/res/network/load-balancer/tests/e2e/max/main.test.bicep +++ b/avm/res/network/load-balancer/tests/e2e/max/main.test.bicep @@ -172,11 +172,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '3a5b2a4a-3584-4d6b-9cf0-ceb1e4f88a5d' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/load-balancer/version.json b/avm/res/network/load-balancer/version.json index daf1a794d9..76049e1c4a 100644 --- a/avm/res/network/load-balancer/version.json +++ b/avm/res/network/load-balancer/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/local-network-gateway/README.md b/avm/res/network/local-network-gateway/README.md index e3892575f0..40ad910b2d 100644 --- a/avm/res/network/local-network-gateway/README.md +++ b/avm/res/network/local-network-gateway/README.md @@ -120,11 +120,13 @@ module localNetworkGateway 'br/public:avm/res/network/local-network-gateway:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -187,11 +189,13 @@ module localNetworkGateway 'br/public:avm/res/network/local-network-gateway:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -459,6 +463,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -509,6 +514,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/local-network-gateway/main.bicep b/avm/res/network/local-network-gateway/main.bicep index dc508ade2d..fd1c0daf10 100644 --- a/avm/res/network/local-network-gateway/main.bicep +++ b/avm/res/network/local-network-gateway/main.bicep @@ -63,6 +63,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -111,14 +122,14 @@ resource localNetworkGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = i } resource localNetworkGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(localNetworkGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + localNetworkGateway.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -155,6 +166,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/local-network-gateway/main.json b/avm/res/network/local-network-gateway/main.json index ccb3c4b6a4..3cbc3596ef 100644 --- a/avm/res/network/local-network-gateway/main.json +++ b/avm/res/network/local-network-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16680341650888375579" + "version": "0.29.47.4906", + "templateHash": "16451984976685243961" }, "name": "Local Network Gateways", "description": "This module deploys a Local Network Gateway.", @@ -43,6 +43,13 @@ "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": { @@ -188,6 +195,13 @@ } }, "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)))))]" + } + ], "bgpSettings": { "asn": "[parameters('localAsn')]", "bgpPeeringAddress": "[parameters('localBgpPeeringAddress')]", @@ -255,20 +269,20 @@ "localNetworkGateway_roleAssignments": { "copy": { "name": "localNetworkGateway_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/localNetworkGateways/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/localNetworkGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/localNetworkGateways', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "localNetworkGateway" diff --git a/avm/res/network/local-network-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/local-network-gateway/tests/e2e/max/main.test.bicep index 2fa14fcdba..edff8a7f59 100644 --- a/avm/res/network/local-network-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/local-network-gateway/tests/e2e/max/main.test.bicep @@ -64,11 +64,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'd14a9fe8-2358-434a-a715-3d10978088cc' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/local-network-gateway/version.json b/avm/res/network/local-network-gateway/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/network/local-network-gateway/version.json +++ b/avm/res/network/local-network-gateway/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/nat-gateway/README.md b/avm/res/network/nat-gateway/README.md index ce59cdb980..dd385b0922 100644 --- a/avm/res/network/nat-gateway/README.md +++ b/avm/res/network/nat-gateway/README.md @@ -19,8 +19,8 @@ This module deploys a NAT Gateway. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/natGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/natGateways) | -| `Microsoft.Network/publicIPAddresses` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/publicIPAddresses) | -| `Microsoft.Network/publicIPPrefixes` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/publicIPPrefixes) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/publicIPPrefixes` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPPrefixes) | ## Usage examples @@ -145,19 +145,21 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = { ] skuTier: 'Regional' zones: [ - '1' - '2' - '3' + 1 + 2 + 3 ] } ] roleAssignments: [ { + name: '69d7ed51-8af4-4eed-bcea-bdadcccb1200' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -243,9 +245,9 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = { ], "skuTier": "Regional", "zones": [ - "1", - "2", - "3" + 1, + 2, + 3 ] } ] @@ -253,11 +255,13 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = { "roleAssignments": { "value": [ { + "name": "69d7ed51-8af4-4eed-bcea-bdadcccb1200", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -396,9 +400,9 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = { name: 'nngwaf001-pip' skuTier: 'Regional' zones: [ - '1' - '2' - '3' + 1 + 2 + 3 ] } ] @@ -460,9 +464,9 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = { "name": "nngwaf001-pip", "skuTier": "Regional", "zones": [ - "1", - "2", - "3" + 1, + 2, + 3 ] } ] @@ -641,6 +645,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -691,6 +696,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -731,8 +743,8 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/public-ip-address:0.2.1` | Remote reference | -| `br/public:avm/res/network/public-ip-prefix:0.1.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.5.1` | Remote reference | +| `br/public:avm/res/network/public-ip-prefix:0.4.1` | Remote reference | ## Data Collection diff --git a/avm/res/network/nat-gateway/main.bicep b/avm/res/network/nat-gateway/main.bicep index 953840ed69..95bfd36cf2 100644 --- a/avm/res/network/nat-gateway/main.bicep +++ b/avm/res/network/nat-gateway/main.bicep @@ -62,6 +62,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -84,7 +95,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -module publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.2.1' = [ +module publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.5.1' = [ for (publicIPAddressObject, index) in (publicIPAddressObjects ?? []): { name: '${uniqueString(deployment().name, location)}-NatGw-PIP-${index}' params: { @@ -99,7 +110,7 @@ module publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.2.1' = [ skuName: 'Standard' // Must be standard skuTier: publicIPAddressObject.?skuTier tags: publicIPAddressObject.?tags ?? tags - zones: publicIPAddressObject.?zones ?? (zone != 0 ? [string(zone)] : null) + zones: publicIPAddressObject.?zones ?? (zone != 0 ? [zone] : null) enableTelemetry: publicIPAddressObject.?enableTelemetry ?? enableTelemetry ddosSettings: publicIPAddressObject.?ddosSettings dnsSettings: publicIPAddressObject.?dnsSettings @@ -118,7 +129,7 @@ module formattedPublicIpResourceIds 'modules/formatResourceId.bicep' = { } } -module publicIPPrefixes 'br/public:avm/res/network/public-ip-prefix:0.1.0' = [ +module publicIPPrefixes 'br/public:avm/res/network/public-ip-prefix:0.4.1' = [ for (publicIPPrefixObject, index) in (publicIPPrefixObjects ?? []): { name: '${uniqueString(deployment().name, location)}-NatGw-Prefix-PIP-${index}' params: { @@ -172,14 +183,10 @@ resource natGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty } resource natGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(natGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(natGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -216,6 +223,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/nat-gateway/main.json b/avm/res/network/nat-gateway/main.json index 8092104112..013f0a6a66 100644 --- a/avm/res/network/nat-gateway/main.json +++ b/avm/res/network/nat-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17516165861349203816" + "version": "0.29.47.4906", + "templateHash": "693015077235905038" }, "name": "NAT Gateways", "description": "This module deploys a NAT Gateway.", @@ -43,6 +43,13 @@ "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": { @@ -194,6 +201,13 @@ } }, "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')]", @@ -261,20 +275,20 @@ "natGateway_roleAssignments": { "copy": { "name": "natGateway_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/natGateways/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/natGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/natGateways', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "natGateway" @@ -326,7 +340,7 @@ "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "zones": { - "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones'), if(not(equals(parameters('zone'), 0)), createArray(string(parameters('zone'))), null()))]" + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones')]" }, "enableTelemetry": { "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" @@ -348,8 +362,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "3488076626994379707" + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -361,6 +375,13 @@ "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": { @@ -398,7 +419,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, "conditionVersion": { @@ -492,11 +513,15 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } } }, + "nullable": true, "metadata": { - "description": "Required. The DDoS protection plan ID associated with the public IP address." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -540,12 +565,19 @@ "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." + "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": { @@ -558,12 +590,19 @@ "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "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": { @@ -644,7 +683,19 @@ }, "zones": { "type": "array", - "nullable": true, + "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." } @@ -744,6 +795,13 @@ } }, "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')]", @@ -761,8 +819,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -780,7 +838,7 @@ }, "publicIpAddress": { "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-04-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -788,7 +846,7 @@ "name": "[parameters('skuName')]", "tier": "[parameters('skuTier')]" }, - "zones": "[parameters('zones')]", + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", "properties": { "ddosSettings": "[parameters('ddosSettings')]", "dnsSettings": "[parameters('dnsSettings')]", @@ -796,7 +854,7 @@ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", - "ipTags": [] + "ipTags": null } }, "publicIpAddress_lock": { @@ -816,20 +874,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -845,12 +903,30 @@ "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')]", - "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", - "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, @@ -886,14 +962,14 @@ "metadata": { "description": "The public IP address of the public IP address resource." }, - "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]" + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]" + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" } } } @@ -928,8 +1004,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16970104100134108268" + "version": "0.29.47.4906", + "templateHash": "15040145942768763519" } }, "parameters": { @@ -1004,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "10775565777623704767" + "version": "0.29.47.4906", + "templateHash": "2051021969117142516" }, "name": "Public IP Prefixes", "description": "This module deploys a Public IP Prefix.", @@ -1042,6 +1118,13 @@ "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": { @@ -1079,7 +1162,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, "conditionVersion": { @@ -1109,7 +1192,7 @@ "type": "string", "minLength": 1, "metadata": { - "description": "Required. Name of the Public IP Prefix." + "description": "Required. The name of the Public IP Prefix." } }, "location": { @@ -1150,7 +1233,26 @@ "type": "object", "defaultValue": {}, "metadata": { - "description": "Optional. The customIpPrefix that this prefix is associated with. A custom IP address prefix is a contiguous range of IP addresses owned by an external customer and provisioned into a subscription. When a custom IP prefix is in Provisioned, Commissioning, or Commissioned state, a linked public IP prefix can be created. Either as a subset of the custom IP prefix range or the entire range." + "description": "Optional. The custom IP address prefix that this prefix is associated with. A custom IP address prefix is a contiguous range of IP addresses owned by an external customer and provisioned into a subscription. When a custom IP prefix is in Provisioned, Commissioning, or Commissioned state, a linked public IP prefix can be created. Either as a subset of the custom IP prefix range or the entire range." + } + }, + "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." } }, "enableTelemetry": { @@ -1162,6 +1264,13 @@ } }, "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')]", @@ -1175,8 +1284,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipprefix.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipprefix.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1194,13 +1303,14 @@ }, "publicIpPrefix": { "type": "Microsoft.Network/publicIPPrefixes", - "apiVersion": "2023-04-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "sku": { "name": "Standard" }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", "properties": { "customIPPrefix": "[if(not(empty(parameters('customIPPrefix'))), parameters('customIPPrefix'), null())]", "publicIPAddressVersion": "IPv4", @@ -1224,20 +1334,20 @@ "publicIpPrefix_roleAssignments": { "copy": { "name": "publicIpPrefix_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPPrefixes/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPPrefixes', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPPrefixes', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "publicIpPrefix" @@ -1271,7 +1381,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpPrefix', '2023-04-01', 'full').location]" + "value": "[reference('publicIpPrefix', '2023-09-01', 'full').location]" } } } @@ -1306,8 +1416,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16970104100134108268" + "version": "0.29.47.4906", + "templateHash": "15040145942768763519" } }, "parameters": { diff --git a/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep index 7a81ab2c81..7d9f27eafb 100644 --- a/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/nat-gateway/tests/e2e/max/main.test.bicep @@ -95,11 +95,7 @@ module testDeployment '../../../main.bicep' = [ } ] skuTier: 'Regional' - zones: [ - '1' - '2' - '3' - ] + zones: [1, 2, 3] diagnosticSettings: [ { name: 'customSetting' @@ -118,11 +114,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '69d7ed51-8af4-4eed-bcea-bdadcccb1200' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep index af09564b21..528dc7537f 100644 --- a/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/nat-gateway/tests/e2e/waf-aligned/main.test.bicep @@ -66,11 +66,7 @@ module testDeployment '../../../main.bicep' = [ { name: '${namePrefix}${serviceShort}001-pip' skuTier: 'Regional' - zones: [ - '1' - '2' - '3' - ] + zones: [1, 2, 3] diagnosticSettings: [ { name: 'customSetting' diff --git a/avm/res/network/nat-gateway/version.json b/avm/res/network/nat-gateway/version.json index 40ec00be0e..6134ccf0a2 100644 --- a/avm/res/network/nat-gateway/version.json +++ b/avm/res/network/nat-gateway/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "1.0", + "version": "1.1", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/network-interface/README.md b/avm/res/network/network-interface/README.md index 1fbdd41fe9..27fee23a36 100644 --- a/avm/res/network/network-interface/README.md +++ b/avm/res/network/network-interface/README.md @@ -150,11 +150,13 @@ module networkInterface 'br/public:avm/res/network/network-interface:' } roleAssignments: [ { + name: '026b830f-441f-469a-8cf3-c3ea9f5bcfe1' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -240,11 +242,13 @@ module networkInterface 'br/public:avm/res/network/network-interface:' "roleAssignments": { "value": [ { + "name": "026b830f-441f-469a-8cf3-c3ea9f5bcfe1", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -736,6 +740,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -786,6 +791,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/network-interface/main.bicep b/avm/res/network/network-interface/main.bicep index d6f889d21b..f708ad2ac2 100644 --- a/avm/res/network/network-interface/main.bicep +++ b/avm/res/network/network-interface/main.bicep @@ -89,6 +89,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-networkinterface.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -210,14 +221,10 @@ resource networkInterface_diagnosticSettings 'Microsoft.Insights/diagnosticSetti ] resource networkInterface_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(networkInterface.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(networkInterface.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -293,6 +300,9 @@ type diagnosticSettingType = { }[]? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/network-interface/main.json b/avm/res/network/network-interface/main.json index 907b99f4b0..c8ccc7adfc 100644 --- a/avm/res/network/network-interface/main.json +++ b/avm/res/network/network-interface/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11394317540950052535" + "version": "0.29.47.4906", + "templateHash": "14406936031965142886" }, "name": "Network Interface", "description": "This module deploys a Network Interface.", @@ -138,6 +138,13 @@ "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": { @@ -340,6 +347,13 @@ } }, "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')]", @@ -462,20 +476,20 @@ "networkInterface_roleAssignments": { "copy": { "name": "networkInterface_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/networkInterfaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/network-interface/tests/e2e/max/main.test.bicep b/avm/res/network/network-interface/tests/e2e/max/main.test.bicep index fd2dd9578f..b490679377 100644 --- a/avm/res/network/network-interface/tests/e2e/max/main.test.bicep +++ b/avm/res/network/network-interface/tests/e2e/max/main.test.bicep @@ -108,11 +108,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '026b830f-441f-469a-8cf3-c3ea9f5bcfe1' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/network-interface/version.json b/avm/res/network/network-interface/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/network-interface/version.json +++ b/avm/res/network/network-interface/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-manager/README.md b/avm/res/network/network-manager/README.md index ecb72993f2..9fae4cde66 100644 --- a/avm/res/network/network-manager/README.md +++ b/avm/res/network/network-manager/README.md @@ -18,14 +18,14 @@ This module deploys a Network Manager. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Network/networkManagers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers) | -| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/connectivityConfigurations) | -| `Microsoft.Network/networkManagers/networkGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/networkGroups) | -| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/networkGroups/staticMembers) | -| `Microsoft.Network/networkManagers/scopeConnections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/scopeConnections) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | +| `Microsoft.Network/networkManagers` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers) | +| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/connectivityConfigurations) | +| `Microsoft.Network/networkManagers/networkGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/networkGroups) | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/networkGroups/staticMembers) | +| `Microsoft.Network/networkManagers/scopeConnections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/scopeConnections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | ## Usage examples @@ -218,11 +218,13 @@ module networkManager 'br/public:avm/res/network/network-manager:' = { ] roleAssignments: [ { + name: 'e8472331-308c-4c77-aa31-017279d8e5b6' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -481,11 +483,13 @@ module networkManager 'br/public:avm/res/network/network-manager:' = { "roleAssignments": { "value": [ { + "name": "e8472331-308c-4c77-aa31-017279d8e5b6", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1089,6 +1093,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1139,6 +1144,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/network-manager/connectivity-configuration/README.md b/avm/res/network/network-manager/connectivity-configuration/README.md index f427ff532d..f3f1eba4fd 100644 --- a/avm/res/network/network-manager/connectivity-configuration/README.md +++ b/avm/res/network/network-manager/connectivity-configuration/README.md @@ -15,7 +15,7 @@ Connectivity configurations define hub-and-spoke or mesh topologies applied to o | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/connectivityConfigurations) | +| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/connectivityConfigurations) | ## Parameters @@ -24,7 +24,7 @@ Connectivity configurations define hub-and-spoke or mesh topologies applied to o | Parameter | Type | Description | | :-- | :-- | :-- | | [`appliesToGroups`](#parameter-appliestogroups) | array | Network Groups for the configuration. A connectivity configuration must be associated to at least one network group. | -| [`connectivityTopology`](#parameter-connectivitytopology) | string | Connectivity topology type. "Mesh" IS CURRENTLY A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION. | +| [`connectivityTopology`](#parameter-connectivitytopology) | string | Connectivity topology type. | | [`name`](#parameter-name) | string | The name of the connectivity configuration. | **Conditional parameters** @@ -100,7 +100,7 @@ Flag if use hub gateway. ### Parameter: `connectivityTopology` -Connectivity topology type. "Mesh" IS CURRENTLY A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION. +Connectivity topology type. - Required: Yes - Type: string diff --git a/avm/res/network/network-manager/connectivity-configuration/main.bicep b/avm/res/network/network-manager/connectivity-configuration/main.bicep index 79fa0f77b4..72e53eb8e0 100644 --- a/avm/res/network/network-manager/connectivity-configuration/main.bicep +++ b/avm/res/network/network-manager/connectivity-configuration/main.bicep @@ -21,7 +21,7 @@ param appliesToGroups appliesToGroupsType 'HubAndSpoke' 'Mesh' ]) -@sys.description('Required. Connectivity topology type. "Mesh" IS CURRENTLY A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION.') +@sys.description('Required. Connectivity topology type.') param connectivityTopology string @sys.description('Conditional. List of hub items. This will create peerings between the specified hub and the virtual networks in the network group specified. Required if connectivityTopology is of type "HubAndSpoke".') @@ -33,11 +33,11 @@ param deleteExistingPeering bool = false @sys.description('Optional. Flag if global mesh is supported. By default, mesh connectivity is applied to virtual networks within the same region. If set to "True", a global mesh enables connectivity across regions.') param isGlobal bool = false -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName } -resource connectivityConfiguration 'Microsoft.Network/networkManagers/connectivityConfigurations@2023-04-01' = { +resource connectivityConfiguration 'Microsoft.Network/networkManagers/connectivityConfigurations@2023-11-01' = { name: name parent: networkManager properties: { diff --git a/avm/res/network/network-manager/connectivity-configuration/main.json b/avm/res/network/network-manager/connectivity-configuration/main.json index 9d606e64e9..df61b0ba14 100644 --- a/avm/res/network/network-manager/connectivity-configuration/main.json +++ b/avm/res/network/network-manager/connectivity-configuration/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "2756544330949607993" + "version": "0.29.47.4906", + "templateHash": "12034113765565179248" }, "name": "Network Manager Connectivity Configurations", - "description": "This module deploys a Network Manager Connectivity Configuration.\r\nConnectivity configurations define hub-and-spoke or mesh topologies applied to one or more network groups.", + "description": "This module deploys a Network Manager Connectivity Configuration.\nConnectivity configurations define hub-and-spoke or mesh topologies applied to one or more network groups.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -111,7 +111,7 @@ "Mesh" ], "metadata": { - "description": "Required. Connectivity topology type. \"Mesh\" IS CURRENTLY A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION." + "description": "Required. Connectivity topology type." } }, "hubs": { @@ -139,12 +139,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "connectivityConfiguration": { "type": "Microsoft.Network/networkManagers/connectivityConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "appliesToGroups": "[map(parameters('appliesToGroups'), lambda('group', createObject('groupConnectivity', lambdaVariables('group').groupConnectivity, 'isGlobal', coalesce(string(lambdaVariables('group').isGlobal), 'false'), 'networkGroupId', lambdaVariables('group').networkGroupResourceId, 'useHubGateway', coalesce(string(lambdaVariables('group').useHubGateway), 'false'))))]", diff --git a/avm/res/network/network-manager/main.bicep b/avm/res/network/network-manager/main.bicep index 897d872ea0..d837037520 100644 --- a/avm/res/network/network-manager/main.bicep +++ b/avm/res/network/network-manager/main.bicep @@ -74,6 +74,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-networkmanager.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -93,7 +104,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' = { name: name location: location tags: tags @@ -172,14 +183,10 @@ resource networkManager_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource networkManager_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(networkManager.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(networkManager.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -216,6 +223,9 @@ type lockType = { }? type roleAssignmentType = { + @sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/network-manager/main.json b/avm/res/network/network-manager/main.json index 3634490442..6b63cf272d 100644 --- a/avm/res/network/network-manager/main.json +++ b/avm/res/network/network-manager/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18324011062979290950" + "version": "0.29.47.4906", + "templateHash": "13151738570302546386" }, "name": "Network Managers", "description": "This module deploys a Network Manager.", @@ -43,6 +43,13 @@ "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": { @@ -638,6 +645,13 @@ } }, "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')]", "IPAM Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b3e853f-ad5d-4fb5-a7b8-56a3581c7037')]", @@ -673,7 +687,7 @@ }, "networkManager": { "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -700,20 +714,20 @@ "networkManager_roleAssignments": { "copy": { "name": "networkManager_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/networkManagers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/networkManagers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkManagers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "networkManager" @@ -753,8 +767,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15382409345741375912" + "version": "0.29.47.4906", + "templateHash": "801055427127865897" }, "name": "Network Manager Network Groups", "description": "This module deploys a Network Manager Network Group.\nA network group is a collection of same-type network resources that you can associate with network manager configurations. You can add same-type network resources after you create the network group.", @@ -816,12 +830,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "networkGroup": { "type": "Microsoft.Network/networkManagers/networkGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]" @@ -863,8 +877,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5856566387911873889" + "version": "0.29.47.4906", + "templateHash": "13401706108335869614" }, "name": "Network Manager Network Group Static Members", "description": "This module deploys a Network Manager Network Group Static Member.\nStatic membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks.", @@ -899,7 +913,7 @@ "resources": [ { "type": "Microsoft.Network/networkManagers/networkGroups/staticMembers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('networkGroupName'), parameters('name'))]", "properties": { "resourceId": "[parameters('resourceId')]" @@ -1012,8 +1026,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10727241272557696879" + "version": "0.29.47.4906", + "templateHash": "12034113765565179248" }, "name": "Network Manager Connectivity Configurations", "description": "This module deploys a Network Manager Connectivity Configuration.\nConnectivity configurations define hub-and-spoke or mesh topologies applied to one or more network groups.", @@ -1118,7 +1132,7 @@ "Mesh" ], "metadata": { - "description": "Required. Connectivity topology type. \"Mesh\" IS CURRENTLY A PREVIEW SERVICE/FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE PRODUCT DOCS FOR CLARIFICATION." + "description": "Required. Connectivity topology type." } }, "hubs": { @@ -1146,12 +1160,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "connectivityConfiguration": { "type": "Microsoft.Network/networkManagers/connectivityConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "appliesToGroups": "[map(parameters('appliesToGroups'), lambda('group', createObject('groupConnectivity', lambdaVariables('group').groupConnectivity, 'isGlobal', coalesce(string(lambdaVariables('group').isGlobal), 'false'), 'networkGroupId', lambdaVariables('group').networkGroupResourceId, 'useHubGateway', coalesce(string(lambdaVariables('group').useHubGateway), 'false'))))]", @@ -1233,8 +1247,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13669533264515087337" + "version": "0.29.47.4906", + "templateHash": "15080082205181838369" }, "name": "Network Manager Scope Connections", "description": "This module deploys a Network Manager Scope Connection.\nCreate a cross-tenant connection to manage a resource from another tenant.", @@ -1279,12 +1293,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "scopeConnection": { "type": "Microsoft.Network/networkManagers/scopeConnections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -1362,8 +1376,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11404005385428137458" + "version": "0.29.47.4906", + "templateHash": "10895784239828351157" }, "name": "Network Manager Security Admin Configurations", "description": "This module deploys an Network Manager Security Admin Configuration.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", @@ -1601,12 +1615,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "securityAdminConfigurations": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -1656,8 +1670,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16935679479200215106" + "version": "0.29.47.4906", + "templateHash": "10012310325610818113" }, "name": "Network Manager Security Admin Configuration Rule Collections", "description": "This module deploys an Network Manager Security Admin Configuration Rule Collection.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.", @@ -1865,7 +1879,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -1874,12 +1888,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "ruleCollection": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -1950,8 +1964,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2308266442236720720" + "version": "0.29.47.4906", + "templateHash": "17218572797495425587" }, "name": "Network Manager Security Admin Configuration Rule Collection Rules", "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", @@ -2137,7 +2151,7 @@ "networkManager::securityAdminConfiguration::ruleCollection": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'))]", "dependsOn": [ "networkManager::securityAdminConfiguration" @@ -2146,7 +2160,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -2155,12 +2169,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "rule": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'), parameters('name'))]", "kind": "Custom", "properties": { @@ -2299,7 +2313,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('networkManager', '2023-04-01', 'full').location]" + "value": "[reference('networkManager', '2023-11-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/network/network-manager/network-group/README.md b/avm/res/network/network-manager/network-group/README.md index 70518686d2..a3800d70d0 100644 --- a/avm/res/network/network-manager/network-group/README.md +++ b/avm/res/network/network-manager/network-group/README.md @@ -15,8 +15,8 @@ A network group is a collection of same-type network resources that you can asso | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/networkGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/networkGroups) | -| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/networkGroups/staticMembers) | +| `Microsoft.Network/networkManagers/networkGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/networkGroups) | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/networkGroups/staticMembers) | ## Parameters diff --git a/avm/res/network/network-manager/network-group/main.bicep b/avm/res/network/network-manager/network-group/main.bicep index d439024f36..31f872a756 100644 --- a/avm/res/network/network-manager/network-group/main.bicep +++ b/avm/res/network/network-manager/network-group/main.bicep @@ -17,11 +17,11 @@ param description string? @sys.description('Optional. Static Members to create for the network group. Contains virtual networks to add to the network group.') param staticMembers staticMembersType -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName } -resource networkGroup 'Microsoft.Network/networkManagers/networkGroups@2023-04-01' = { +resource networkGroup 'Microsoft.Network/networkManagers/networkGroups@2023-11-01' = { name: name parent: networkManager properties: { diff --git a/avm/res/network/network-manager/network-group/main.json b/avm/res/network/network-manager/network-group/main.json index c9beddab5a..f1a3507779 100644 --- a/avm/res/network/network-manager/network-group/main.json +++ b/avm/res/network/network-manager/network-group/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1167650749257240939" + "version": "0.29.47.4906", + "templateHash": "801055427127865897" }, "name": "Network Manager Network Groups", - "description": "This module deploys a Network Manager Network Group.\r\nA network group is a collection of same-type network resources that you can associate with network manager configurations. You can add same-type network resources after you create the network group.", + "description": "This module deploys a Network Manager Network Group.\nA network group is a collection of same-type network resources that you can associate with network manager configurations. You can add same-type network resources after you create the network group.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -68,12 +68,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "networkGroup": { "type": "Microsoft.Network/networkManagers/networkGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]" @@ -115,11 +115,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15788464740461325450" + "version": "0.29.47.4906", + "templateHash": "13401706108335869614" }, "name": "Network Manager Network Group Static Members", - "description": "This module deploys a Network Manager Network Group Static Member.\r\nStatic membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks.", + "description": "This module deploys a Network Manager Network Group Static Member.\nStatic membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -151,7 +151,7 @@ "resources": [ { "type": "Microsoft.Network/networkManagers/networkGroups/staticMembers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('networkGroupName'), parameters('name'))]", "properties": { "resourceId": "[parameters('resourceId')]" diff --git a/avm/res/network/network-manager/network-group/static-member/README.md b/avm/res/network/network-manager/network-group/static-member/README.md index 7cec1b2dc5..54221ad22f 100644 --- a/avm/res/network/network-manager/network-group/static-member/README.md +++ b/avm/res/network/network-manager/network-group/static-member/README.md @@ -15,7 +15,7 @@ Static membership allows you to explicitly add virtual networks to a group by ma | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/networkGroups/staticMembers) | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/networkGroups/staticMembers) | ## Parameters diff --git a/avm/res/network/network-manager/network-group/static-member/main.bicep b/avm/res/network/network-manager/network-group/static-member/main.bicep index 7a27966bb0..2394d101c9 100644 --- a/avm/res/network/network-manager/network-group/static-member/main.bicep +++ b/avm/res/network/network-manager/network-group/static-member/main.bicep @@ -15,15 +15,15 @@ param name string @description('Required. Resource ID of the virtual network.') param resourceId string -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName - resource networkGroup 'networkGroups@2023-04-01' existing = { + resource networkGroup 'networkGroups@2023-11-01' existing = { name: networkGroupName } } -resource staticMember 'Microsoft.Network/networkManagers/networkGroups/staticMembers@2023-04-01' = { +resource staticMember 'Microsoft.Network/networkManagers/networkGroups/staticMembers@2023-11-01' = { name: name parent: networkManager::networkGroup properties: { diff --git a/avm/res/network/network-manager/network-group/static-member/main.json b/avm/res/network/network-manager/network-group/static-member/main.json index 2da8c12013..9fa6afbfbb 100644 --- a/avm/res/network/network-manager/network-group/static-member/main.json +++ b/avm/res/network/network-manager/network-group/static-member/main.json @@ -4,11 +4,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "15788464740461325450" + "version": "0.29.47.4906", + "templateHash": "13401706108335869614" }, "name": "Network Manager Network Group Static Members", - "description": "This module deploys a Network Manager Network Group Static Member.\r\nStatic membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks.", + "description": "This module deploys a Network Manager Network Group Static Member.\nStatic membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -40,7 +40,7 @@ "resources": [ { "type": "Microsoft.Network/networkManagers/networkGroups/staticMembers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('networkGroupName'), parameters('name'))]", "properties": { "resourceId": "[parameters('resourceId')]" diff --git a/avm/res/network/network-manager/scope-connection/README.md b/avm/res/network/network-manager/scope-connection/README.md index 3c998e0577..7b516e3481 100644 --- a/avm/res/network/network-manager/scope-connection/README.md +++ b/avm/res/network/network-manager/scope-connection/README.md @@ -15,7 +15,7 @@ Create a cross-tenant connection to manage a resource from another tenant. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/scopeConnections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/scopeConnections) | +| `Microsoft.Network/networkManagers/scopeConnections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/scopeConnections) | ## Parameters diff --git a/avm/res/network/network-manager/scope-connection/main.bicep b/avm/res/network/network-manager/scope-connection/main.bicep index cb54a5e34e..68c7bf5b4a 100644 --- a/avm/res/network/network-manager/scope-connection/main.bicep +++ b/avm/res/network/network-manager/scope-connection/main.bicep @@ -20,11 +20,11 @@ param resourceId string @sys.description('Required. Tenant ID of the subscription or management group that you want to manage.') param tenantId string -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName } -resource scopeConnection 'Microsoft.Network/networkManagers/scopeConnections@2023-04-01' = { +resource scopeConnection 'Microsoft.Network/networkManagers/scopeConnections@2023-11-01' = { name: name parent: networkManager properties: { diff --git a/avm/res/network/network-manager/scope-connection/main.json b/avm/res/network/network-manager/scope-connection/main.json index f0a9fec4dc..ec24288164 100644 --- a/avm/res/network/network-manager/scope-connection/main.json +++ b/avm/res/network/network-manager/scope-connection/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13509671695440625631" + "version": "0.29.47.4906", + "templateHash": "15080082205181838369" }, "name": "Network Manager Scope Connections", - "description": "This module deploys a Network Manager Scope Connection.\r\nCreate a cross-tenant connection to manage a resource from another tenant.", + "description": "This module deploys a Network Manager Scope Connection.\nCreate a cross-tenant connection to manage a resource from another tenant.", "owner": "Azure/module-maintainers" }, "parameters": { @@ -51,12 +51,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "scopeConnection": { "type": "Microsoft.Network/networkManagers/scopeConnections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", diff --git a/avm/res/network/network-manager/security-admin-configuration/README.md b/avm/res/network/network-manager/security-admin-configuration/README.md index adbe7caa4b..0b3efba343 100644 --- a/avm/res/network/network-manager/security-admin-configuration/README.md +++ b/avm/res/network/network-manager/security-admin-configuration/README.md @@ -15,9 +15,9 @@ A security admin configuration contains a set of rule collections. Each rule col | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | ## Parameters diff --git a/avm/res/network/network-manager/security-admin-configuration/main.bicep b/avm/res/network/network-manager/security-admin-configuration/main.bicep index 02d8b63e16..7f0c96f910 100644 --- a/avm/res/network/network-manager/security-admin-configuration/main.bicep +++ b/avm/res/network/network-manager/security-admin-configuration/main.bicep @@ -20,11 +20,11 @@ param applyOnNetworkIntentPolicyBasedServices applyOnNetworkIntentPolicyBasedSer @sys.description('Optional. A security admin configuration contains a set of rule collections that are applied to network groups. Each rule collection contains one or more security admin rules.') param ruleCollections ruleCollectionType -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName } -resource securityAdminConfigurations 'Microsoft.Network/networkManagers/securityAdminConfigurations@2023-04-01' = { +resource securityAdminConfigurations 'Microsoft.Network/networkManagers/securityAdminConfigurations@2023-11-01' = { name: name parent: networkManager properties: { diff --git a/avm/res/network/network-manager/security-admin-configuration/main.json b/avm/res/network/network-manager/security-admin-configuration/main.json index e890aca4d4..e032b526fd 100644 --- a/avm/res/network/network-manager/security-admin-configuration/main.json +++ b/avm/res/network/network-manager/security-admin-configuration/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "11015814946289049349" + "version": "0.29.47.4906", + "templateHash": "10895784239828351157" }, "name": "Network Manager Security Admin Configurations", - "description": "This module deploys an Network Manager Security Admin Configuration.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", + "description": "This module deploys an Network Manager Security Admin Configuration.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -244,12 +244,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "securityAdminConfigurations": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -299,11 +299,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4261286839823300724" + "version": "0.29.47.4906", + "templateHash": "10012310325610818113" }, "name": "Network Manager Security Admin Configuration Rule Collections", - "description": "This module deploys an Network Manager Security Admin Configuration Rule Collection.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.", + "description": "This module deploys an Network Manager Security Admin Configuration Rule Collection.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -508,7 +508,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -517,12 +517,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "ruleCollection": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -593,11 +593,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17880704693619791217" + "version": "0.29.47.4906", + "templateHash": "17218572797495425587" }, "name": "Network Manager Security Admin Configuration Rule Collection Rules", - "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", + "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -780,7 +780,7 @@ "networkManager::securityAdminConfiguration::ruleCollection": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'))]", "dependsOn": [ "networkManager::securityAdminConfiguration" @@ -789,7 +789,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -798,12 +798,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "rule": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'), parameters('name'))]", "kind": "Custom", "properties": { diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/README.md b/avm/res/network/network-manager/security-admin-configuration/rule-collection/README.md index e2c96d3d7b..684047ed54 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/README.md +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/README.md @@ -15,8 +15,8 @@ A security admin configuration contains a set of rule collections. Each rule col | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections) | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | ## Parameters diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.bicep b/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.bicep index 901eebf589..3996f08ff7 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.bicep +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.bicep @@ -23,15 +23,15 @@ param appliesToGroups appliesToGroupsType @sys.description('Optional. List of rules for the admin rules collection. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.') param rules rulesType -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName - resource securityAdminConfiguration 'securityAdminConfigurations@2023-04-01' existing = { + resource securityAdminConfiguration 'securityAdminConfigurations@2023-11-01' existing = { name: securityAdminConfigurationName } } -resource ruleCollection 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections@2023-04-01' = { +resource ruleCollection 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections@2023-11-01' = { name: name parent: networkManager::securityAdminConfiguration properties: { diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.json b/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.json index b538b03068..79b3cfd98c 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.json +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "4261286839823300724" + "version": "0.29.47.4906", + "templateHash": "10012310325610818113" }, "name": "Network Manager Security Admin Configuration Rule Collections", - "description": "This module deploys an Network Manager Security Admin Configuration Rule Collection.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.", + "description": "This module deploys an Network Manager Security Admin Configuration Rule Collection.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -214,7 +214,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -223,12 +223,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "ruleCollection": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('name'))]", "properties": { "description": "[coalesce(parameters('description'), '')]", @@ -299,11 +299,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17880704693619791217" + "version": "0.29.47.4906", + "templateHash": "17218572797495425587" }, "name": "Network Manager Security Admin Configuration Rule Collection Rules", - "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", + "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -486,7 +486,7 @@ "networkManager::securityAdminConfiguration::ruleCollection": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'))]", "dependsOn": [ "networkManager::securityAdminConfiguration" @@ -495,7 +495,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -504,12 +504,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "rule": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'), parameters('name'))]", "kind": "Custom", "properties": { diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/README.md b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/README.md index 1b68b0ca8f..a9ed2be00a 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/README.md +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/README.md @@ -15,7 +15,7 @@ A security admin configuration contains a set of rule collections. Each rule col | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | ## Parameters diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.bicep b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.bicep index 82d166e5e8..9a4af7e812 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.bicep +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.bicep @@ -63,19 +63,19 @@ param sourcePortRanges string[]? @sys.description('Optional. The source filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted.') param sources sourcesType -resource networkManager 'Microsoft.Network/networkManagers@2023-04-01' existing = { +resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = { name: networkManagerName - resource securityAdminConfiguration 'securityAdminConfigurations@2023-04-01' existing = { + resource securityAdminConfiguration 'securityAdminConfigurations@2023-11-01' existing = { name: securityAdminConfigurationName - resource ruleCollection 'ruleCollections@2023-04-01' existing = { + resource ruleCollection 'ruleCollections@2023-11-01' existing = { name: ruleCollectionName } } } -resource rule 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules@2023-04-01' = { +resource rule 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules@2023-11-01' = { name: name parent: networkManager::securityAdminConfiguration::ruleCollection kind: 'Custom' diff --git a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.json b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.json index 4dd8684e1f..1cf0e8035c 100644 --- a/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.json +++ b/avm/res/network/network-manager/security-admin-configuration/rule-collection/rule/main.json @@ -5,11 +5,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17880704693619791217" + "version": "0.29.47.4906", + "templateHash": "17218572797495425587" }, "name": "Network Manager Security Admin Configuration Rule Collection Rules", - "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\r\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", + "description": "This module deploys an Azure Virtual Network Manager (AVNM) Security Admin Configuration Rule Collection Rule.\nA security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules.", "owner": "Azure/module-maintainers" }, "definitions": { @@ -192,7 +192,7 @@ "networkManager::securityAdminConfiguration::ruleCollection": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'))]", "dependsOn": [ "networkManager::securityAdminConfiguration" @@ -201,7 +201,7 @@ "networkManager::securityAdminConfiguration": { "existing": true, "type": "Microsoft.Network/networkManagers/securityAdminConfigurations", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'))]", "dependsOn": [ "networkManager" @@ -210,12 +210,12 @@ "networkManager": { "existing": true, "type": "Microsoft.Network/networkManagers", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('networkManagerName')]" }, "rule": { "type": "Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}/{2}/{3}', parameters('networkManagerName'), parameters('securityAdminConfigurationName'), parameters('ruleCollectionName'), parameters('name'))]", "kind": "Custom", "properties": { diff --git a/avm/res/network/network-manager/tests/e2e/max/main.test.bicep b/avm/res/network/network-manager/tests/e2e/max/main.test.bicep index af304501bb..fe3ef45227 100644 --- a/avm/res/network/network-manager/tests/e2e/max/main.test.bicep +++ b/avm/res/network/network-manager/tests/e2e/max/main.test.bicep @@ -65,11 +65,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'e8472331-308c-4c77-aa31-017279d8e5b6' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/network-manager/version.json b/avm/res/network/network-manager/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/network/network-manager/version.json +++ b/avm/res/network/network-manager/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-security-group/README.md b/avm/res/network/network-security-group/README.md index 57e3c63bb2..7befc2d1a7 100644 --- a/avm/res/network/network-security-group/README.md +++ b/avm/res/network/network-security-group/README.md @@ -112,11 +112,13 @@ module networkSecurityGroup 'br/public:avm/res/network/network-security-group:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -265,11 +267,13 @@ module networkSecurityGroup 'br/public:avm/res/network/network-security-group:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -707,6 +711,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -757,6 +762,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/network-security-group/main.bicep b/avm/res/network/network-security-group/main.bicep index b3351a40fb..ad4989e7ee 100644 --- a/avm/res/network/network-security-group/main.bicep +++ b/avm/res/network/network-security-group/main.bicep @@ -47,6 +47,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-networksecuritygroup.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -141,14 +152,14 @@ resource networkSecurityGroup_diagnosticSettings 'Microsoft.Insights/diagnosticS ] resource networkSecurityGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(networkSecurityGroup.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + networkSecurityGroup.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -185,6 +196,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/network-security-group/main.json b/avm/res/network/network-security-group/main.json index 0ccad149ff..5dbc84d508 100644 --- a/avm/res/network/network-security-group/main.json +++ b/avm/res/network/network-security-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8937326119878739537" + "version": "0.29.47.4906", + "templateHash": "3065442976821754784" }, "name": "Network Security Groups", "description": "This module deploys a Network security Group (NSG).", @@ -43,6 +43,13 @@ "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": { @@ -421,6 +428,13 @@ } }, "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')]", @@ -536,20 +550,20 @@ "networkSecurityGroup_roleAssignments": { "copy": { "name": "networkSecurityGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/network-security-group/tests/e2e/max/main.test.bicep b/avm/res/network/network-security-group/tests/e2e/max/main.test.bicep index 8dde6bb210..3e54aa0338 100644 --- a/avm/res/network/network-security-group/tests/e2e/max/main.test.bicep +++ b/avm/res/network/network-security-group/tests/e2e/max/main.test.bicep @@ -82,11 +82,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'b6d38ee8-4058-42b1-af6a-b8d585cf61ef' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/network-security-group/version.json b/avm/res/network/network-security-group/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/network/network-security-group/version.json +++ b/avm/res/network/network-security-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-watcher/README.md b/avm/res/network/network-watcher/README.md index b415feec56..6d428b6232 100644 --- a/avm/res/network/network-watcher/README.md +++ b/avm/res/network/network-watcher/README.md @@ -159,11 +159,13 @@ module networkWatcher 'br/public:avm/res/network/network-watcher:' = { name: '' roleAssignments: [ { + name: 'e8e93fb7-f450-41d5-ae86-a32d34e72578' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -277,11 +279,13 @@ module networkWatcher 'br/public:avm/res/network/network-watcher:' = { "roleAssignments": { "value": [ { + "name": "e8e93fb7-f450-41d5-ae86-a32d34e72578", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -619,6 +623,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -669,6 +674,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/network-watcher/connection-monitor/main.json b/avm/res/network/network-watcher/connection-monitor/main.json index 3d9fe1be89..499727cc05 100644 --- a/avm/res/network/network-watcher/connection-monitor/main.json +++ b/avm/res/network/network-watcher/connection-monitor/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "14911705346376844157" + "version": "0.29.47.4906", + "templateHash": "1395789016631295523" }, "name": "Network Watchers Connection Monitors", "description": "This module deploys a Network Watcher Connection Monitor.", diff --git a/avm/res/network/network-watcher/flow-log/main.json b/avm/res/network/network-watcher/flow-log/main.json index 3727d61a85..625358d232 100644 --- a/avm/res/network/network-watcher/flow-log/main.json +++ b/avm/res/network/network-watcher/flow-log/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3648590963487326602" + "version": "0.29.47.4906", + "templateHash": "2231753294747447946" }, "name": "NSG Flow Logs", "description": "This module controls the Network Security Group Flow Logs and analytics settings.\n**Note: this module must be run on the Resource Group where Network Watcher is deployed**", diff --git a/avm/res/network/network-watcher/main.bicep b/avm/res/network/network-watcher/main.bicep index d8966a83aa..5a4f0922c7 100644 --- a/avm/res/network/network-watcher/main.bicep +++ b/avm/res/network/network-watcher/main.bicep @@ -45,6 +45,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-networkwatcher.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -83,14 +94,10 @@ resource networkWatcher_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource networkWatcher_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(networkWatcher.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(networkWatcher.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -164,6 +171,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/network-watcher/main.json b/avm/res/network/network-watcher/main.json index be0b207004..de9c03a23f 100644 --- a/avm/res/network/network-watcher/main.json +++ b/avm/res/network/network-watcher/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12849012008844484104" + "version": "0.29.47.4906", + "templateHash": "4213182635541317851" }, "name": "Network Watchers", "description": "This module deploys a Network Watcher.", @@ -43,6 +43,13 @@ "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": { @@ -163,6 +170,13 @@ } }, "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')]", @@ -218,20 +232,20 @@ "networkWatcher_roleAssignments": { "copy": { "name": "networkWatcher_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/networkWatchers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/networkWatchers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkWatchers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "networkWatcher" @@ -272,8 +286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10444358838090684247" + "version": "0.29.47.4906", + "templateHash": "1395789016631295523" }, "name": "Network Watchers Connection Monitors", "description": "This module deploys a Network Watcher Connection Monitor.", @@ -434,8 +448,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3763497651222221236" + "version": "0.29.47.4906", + "templateHash": "2231753294747447946" }, "name": "NSG Flow Logs", "description": "This module controls the Network Security Group Flow Logs and analytics settings.\n**Note: this module must be run on the Resource Group where Network Watcher is deployed**", diff --git a/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep b/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep index 520f9b79e2..3666a62509 100644 --- a/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep +++ b/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep @@ -146,11 +146,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'e8e93fb7-f450-41d5-ae86-a32d34e72578' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/network-watcher/version.json b/avm/res/network/network-watcher/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/network/network-watcher/version.json +++ b/avm/res/network/network-watcher/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/private-dns-zone/README.md b/avm/res/network/private-dns-zone/README.md index 379b8aebd4..081049a660 100644 --- a/avm/res/network/private-dns-zone/README.md +++ b/avm/res/network/private-dns-zone/README.md @@ -233,11 +233,13 @@ module privateDnsZone 'br/public:avm/res/network/private-dns-zone:' = { ] roleAssignments: [ { + name: '8001f03c-2ca1-4dab-ab69-4dbaa3635af1' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -517,11 +519,13 @@ module privateDnsZone 'br/public:avm/res/network/private-dns-zone:' = { "roleAssignments": { "value": [ { + "name": "8001f03c-2ca1-4dab-ab69-4dbaa3635af1", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -837,6 +841,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-aroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-aroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-aroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-aroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-aroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `a.roleAssignments.principalId` @@ -887,6 +892,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `a.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `a.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -989,6 +1001,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-aaaaroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-aaaaroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-aaaaroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-aaaaroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-aaaaroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `aaaa.roleAssignments.principalId` @@ -1039,6 +1052,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `aaaa.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `aaaa.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1141,6 +1161,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-cnameroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-cnameroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-cnameroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-cnameroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-cnameroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `cname.roleAssignments.principalId` @@ -1191,6 +1212,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `cname.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `cname.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1353,6 +1381,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-mxroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-mxroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-mxroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-mxroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-mxroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `mx.roleAssignments.principalId` @@ -1403,6 +1432,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `mx.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `mx.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1505,6 +1541,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-ptrroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-ptrroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-ptrroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-ptrroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-ptrroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `ptr.roleAssignments.principalId` @@ -1555,6 +1592,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `ptr.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `ptr.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1601,6 +1645,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1651,6 +1696,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1726,6 +1778,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-soaroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-soaroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-soaroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-soaroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-soaroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `soa.roleAssignments.principalId` @@ -1776,6 +1829,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `soa.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `soa.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1926,6 +1986,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-srvroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-srvroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-srvroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-srvroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-srvroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `srv.roleAssignments.principalId` @@ -1976,6 +2037,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `srv.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `srv.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2109,6 +2177,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-txtroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-txtroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-txtroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-txtroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-txtroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `txt.roleAssignments.principalId` @@ -2159,6 +2228,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `txt.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `txt.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2210,6 +2286,56 @@ Array of custom objects describing vNet links of the DNS zone. Each object shoul - Required: No - Type: array +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`virtualNetworkResourceId`](#parameter-virtualnetworklinksvirtualnetworkresourceid) | string | The resource ID of the virtual network to link. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-virtualnetworklinkslocation) | string | The Azure Region where the resource lives. | +| [`name`](#parameter-virtualnetworklinksname) | string | The resource name. | +| [`registrationEnabled`](#parameter-virtualnetworklinksregistrationenabled) | bool | Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?. | +| [`tags`](#parameter-virtualnetworklinkstags) | object | Resource tags. | + +### Parameter: `virtualNetworkLinks.virtualNetworkResourceId` + +The resource ID of the virtual network to link. + +- Required: Yes +- Type: string + +### Parameter: `virtualNetworkLinks.location` + +The Azure Region where the resource lives. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkLinks.name` + +The resource name. + +- Required: No +- Type: string + +### Parameter: `virtualNetworkLinks.registrationEnabled` + +Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?. + +- Required: No +- Type: bool + +### Parameter: `virtualNetworkLinks.tags` + +Resource tags. + +- Required: No +- Type: object + ## Outputs diff --git a/avm/res/network/private-dns-zone/a/README.md b/avm/res/network/private-dns-zone/a/README.md index 10d716502b..a4a8230e7f 100644 --- a/avm/res/network/private-dns-zone/a/README.md +++ b/avm/res/network/private-dns-zone/a/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/a/main.bicep b/avm/res/network/private-dns-zone/a/main.bicep index 9804d60427..a45dd730e9 100644 --- a/avm/res/network/private-dns-zone/a/main.bicep +++ b/avm/res/network/private-dns-zone/a/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource A 'Microsoft.Network/privateDnsZones/A@2020-06-01' = { } resource A_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(A.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(A.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/a/main.json b/avm/res/network/private-dns-zone/a/main.json index 0e1c3e765d..3e788addc2 100644 --- a/avm/res/network/private-dns-zone/a/main.json +++ b/avm/res/network/private-dns-zone/a/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1479424254286113961" + "version": "0.29.47.4906", + "templateHash": "11343565859504250241" }, "name": "Private DNS Zone A record", "description": "This module deploys a Private DNS Zone A record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "A_roleAssignments": { "copy": { "name": "A_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/aaaa/README.md b/avm/res/network/private-dns-zone/aaaa/README.md index 4070b68358..3bab9dfaa7 100644 --- a/avm/res/network/private-dns-zone/aaaa/README.md +++ b/avm/res/network/private-dns-zone/aaaa/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/aaaa/main.bicep b/avm/res/network/private-dns-zone/aaaa/main.bicep index 00c814df6a..3ebf64ba21 100644 --- a/avm/res/network/private-dns-zone/aaaa/main.bicep +++ b/avm/res/network/private-dns-zone/aaaa/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource AAAA 'Microsoft.Network/privateDnsZones/AAAA@2020-06-01' = { } resource AAAA_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(AAAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(AAAA.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/aaaa/main.json b/avm/res/network/private-dns-zone/aaaa/main.json index e53148bf49..c6b95cc2f8 100644 --- a/avm/res/network/private-dns-zone/aaaa/main.json +++ b/avm/res/network/private-dns-zone/aaaa/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9900490286335618416" + "version": "0.29.47.4906", + "templateHash": "3700934406905797316" }, "name": "Private DNS Zone AAAA record", "description": "This module deploys a Private DNS Zone AAAA record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "AAAA_roleAssignments": { "copy": { "name": "AAAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/cname/README.md b/avm/res/network/private-dns-zone/cname/README.md index bf3ac470eb..8988e5b7e1 100644 --- a/avm/res/network/private-dns-zone/cname/README.md +++ b/avm/res/network/private-dns-zone/cname/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/cname/main.bicep b/avm/res/network/private-dns-zone/cname/main.bicep index 95349d785c..cb2e472857 100644 --- a/avm/res/network/private-dns-zone/cname/main.bicep +++ b/avm/res/network/private-dns-zone/cname/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource CNAME 'Microsoft.Network/privateDnsZones/CNAME@2020-06-01' = { } resource CNAME_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(CNAME.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(CNAME.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/cname/main.json b/avm/res/network/private-dns-zone/cname/main.json index 00479a4feb..70fa98a182 100644 --- a/avm/res/network/private-dns-zone/cname/main.json +++ b/avm/res/network/private-dns-zone/cname/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "9775157141161355944" + "version": "0.29.47.4906", + "templateHash": "12929118683431932277" }, "name": "Private DNS Zone CNAME record", "description": "This module deploys a Private DNS Zone CNAME record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "CNAME_roleAssignments": { "copy": { "name": "CNAME_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/main.bicep b/avm/res/network/private-dns-zone/main.bicep index cdccd428b8..2c2f71c0e5 100644 --- a/avm/res/network/private-dns-zone/main.bicep +++ b/avm/res/network/private-dns-zone/main.bicep @@ -30,7 +30,7 @@ param srv srvType param txt txtType @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.') -param virtualNetworkLinks array? +param virtualNetworkLinks virtualNetworkLinkType @description('Optional. The location of the PrivateDNSZone. Should be global.') param location string = 'global' @@ -65,6 +65,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-privatednszone.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -228,14 +239,10 @@ resource privateDnsZone_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource privateDnsZone_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(privateDnsZone.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(privateDnsZone.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -264,6 +271,9 @@ output location string = privateDnsZone.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -483,3 +493,22 @@ type txtType = { value: string[] }[]? }[]? + +type virtualNetworkLinkType = { + @description('Optional. The resource name.') + @minLength(1) + @maxLength(80) + name: string? + + @description('Required. The resource ID of the virtual network to link.') + virtualNetworkResourceId: string + + @description('Optional. The Azure Region where the resource lives.') + location: string? + + @description('Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?.') + registrationEnabled: bool? + + @description('Optional. Resource tags.') + tags: object? +}[]? diff --git a/avm/res/network/private-dns-zone/main.json b/avm/res/network/private-dns-zone/main.json index 39b8165736..a55804ff6f 100644 --- a/avm/res/network/private-dns-zone/main.json +++ b/avm/res/network/private-dns-zone/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2045878030011082737" + "version": "0.29.47.4906", + "templateHash": "16905725400119435284" }, "name": "Private DNS Zones", "description": "This module deploys a Private DNS zone.", @@ -18,6 +18,13 @@ "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": { @@ -584,6 +591,51 @@ } }, "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -642,8 +694,7 @@ } }, "virtualNetworkLinks": { - "type": "array", - "nullable": true, + "$ref": "#/definitions/virtualNetworkLinkType", "metadata": { "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." } @@ -683,6 +734,13 @@ } }, "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')]", @@ -737,20 +795,20 @@ "privateDnsZone_roleAssignments": { "copy": { "name": "privateDnsZone_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -796,8 +854,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8657463061873174631" + "version": "0.29.47.4906", + "templateHash": "11343565859504250241" }, "name": "Private DNS Zone A record", "description": "This module deploys a Private DNS Zone A record.", @@ -809,6 +867,13 @@ "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": { @@ -913,6 +978,13 @@ } }, "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')]", @@ -946,20 +1018,20 @@ "A_roleAssignments": { "copy": { "name": "A_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1035,8 +1107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1725749611610769138" + "version": "0.29.47.4906", + "templateHash": "3700934406905797316" }, "name": "Private DNS Zone AAAA record", "description": "This module deploys a Private DNS Zone AAAA record.", @@ -1048,6 +1120,13 @@ "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": { @@ -1152,6 +1231,13 @@ } }, "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')]", @@ -1185,20 +1271,20 @@ "AAAA_roleAssignments": { "copy": { "name": "AAAA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1274,8 +1360,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12750497126800708872" + "version": "0.29.47.4906", + "templateHash": "12929118683431932277" }, "name": "Private DNS Zone CNAME record", "description": "This module deploys a Private DNS Zone CNAME record.", @@ -1287,6 +1373,13 @@ "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": { @@ -1391,6 +1484,13 @@ } }, "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')]", @@ -1424,20 +1524,20 @@ "CNAME_roleAssignments": { "copy": { "name": "CNAME_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1513,8 +1613,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13512848802819943036" + "version": "0.29.47.4906", + "templateHash": "14178508926823177155" }, "name": "Private DNS Zone MX record", "description": "This module deploys a Private DNS Zone MX record.", @@ -1526,6 +1626,13 @@ "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": { @@ -1630,6 +1737,13 @@ } }, "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')]", @@ -1663,20 +1777,20 @@ "MX_roleAssignments": { "copy": { "name": "MX_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1752,8 +1866,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5069219418696231980" + "version": "0.29.47.4906", + "templateHash": "8082128465097964607" }, "name": "Private DNS Zone PTR record", "description": "This module deploys a Private DNS Zone PTR record.", @@ -1765,6 +1879,13 @@ "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": { @@ -1869,6 +1990,13 @@ } }, "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')]", @@ -1902,20 +2030,20 @@ "PTR_roleAssignments": { "copy": { "name": "PTR_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1991,8 +2119,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8649693365938652763" + "version": "0.29.47.4906", + "templateHash": "6755707328778392735" }, "name": "Private DNS Zone SOA record", "description": "This module deploys a Private DNS Zone SOA record.", @@ -2004,6 +2132,13 @@ "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": { @@ -2108,6 +2243,13 @@ } }, "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')]", @@ -2141,20 +2283,20 @@ "SOA_roleAssignments": { "copy": { "name": "SOA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2230,8 +2372,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1580431299497536987" + "version": "0.29.47.4906", + "templateHash": "662436323695062210" }, "name": "Private DNS Zone SRV record", "description": "This module deploys a Private DNS Zone SRV record.", @@ -2243,6 +2385,13 @@ "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": { @@ -2347,6 +2496,13 @@ } }, "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')]", @@ -2380,20 +2536,20 @@ "SRV_roleAssignments": { "copy": { "name": "SRV_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2469,8 +2625,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1628766837655887758" + "version": "0.29.47.4906", + "templateHash": "356727884506799436" }, "name": "Private DNS Zone TXT record", "description": "This module deploys a Private DNS Zone TXT record.", @@ -2482,6 +2638,13 @@ "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": { @@ -2586,6 +2749,13 @@ } }, "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')]", @@ -2619,20 +2789,20 @@ "TXT_roleAssignments": { "copy": { "name": "TXT_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2708,8 +2878,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8423159394395901832" + "version": "0.29.47.4906", + "templateHash": "1713449351614683457" }, "name": "Private DNS Zone Virtual Network Link", "description": "This module deploys a Private DNS Zone Virtual Network Link.", diff --git a/avm/res/network/private-dns-zone/mx/README.md b/avm/res/network/private-dns-zone/mx/README.md index 9934eaf963..73f2f3d545 100644 --- a/avm/res/network/private-dns-zone/mx/README.md +++ b/avm/res/network/private-dns-zone/mx/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/mx/main.bicep b/avm/res/network/private-dns-zone/mx/main.bicep index 3430a6af63..e9bab6df30 100644 --- a/avm/res/network/private-dns-zone/mx/main.bicep +++ b/avm/res/network/private-dns-zone/mx/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource MX 'Microsoft.Network/privateDnsZones/MX@2020-06-01' = { } resource MX_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(MX.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(MX.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/mx/main.json b/avm/res/network/private-dns-zone/mx/main.json index 2a4b5ee32f..70afeb7694 100644 --- a/avm/res/network/private-dns-zone/mx/main.json +++ b/avm/res/network/private-dns-zone/mx/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12759370349231900780" + "version": "0.29.47.4906", + "templateHash": "14178508926823177155" }, "name": "Private DNS Zone MX record", "description": "This module deploys a Private DNS Zone MX record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "MX_roleAssignments": { "copy": { "name": "MX_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/ptr/README.md b/avm/res/network/private-dns-zone/ptr/README.md index 4d3b8c268f..e6b424b696 100644 --- a/avm/res/network/private-dns-zone/ptr/README.md +++ b/avm/res/network/private-dns-zone/ptr/README.md @@ -90,6 +90,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -140,6 +141,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/ptr/main.bicep b/avm/res/network/private-dns-zone/ptr/main.bicep index 7343b754fb..f00fed8516 100644 --- a/avm/res/network/private-dns-zone/ptr/main.bicep +++ b/avm/res/network/private-dns-zone/ptr/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource PTR 'Microsoft.Network/privateDnsZones/PTR@2020-06-01' = { } resource PTR_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(PTR.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(PTR.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/ptr/main.json b/avm/res/network/private-dns-zone/ptr/main.json index 0f99433ffc..a2ae83cb73 100644 --- a/avm/res/network/private-dns-zone/ptr/main.json +++ b/avm/res/network/private-dns-zone/ptr/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13592920041250204501" + "version": "0.29.47.4906", + "templateHash": "8082128465097964607" }, "name": "Private DNS Zone PTR record", "description": "This module deploys a Private DNS Zone PTR record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "PTR_roleAssignments": { "copy": { "name": "PTR_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/soa/README.md b/avm/res/network/private-dns-zone/soa/README.md index b55ff42d69..b288aba6ad 100644 --- a/avm/res/network/private-dns-zone/soa/README.md +++ b/avm/res/network/private-dns-zone/soa/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/soa/main.bicep b/avm/res/network/private-dns-zone/soa/main.bicep index 34f5b558da..b5df6a6b5e 100644 --- a/avm/res/network/private-dns-zone/soa/main.bicep +++ b/avm/res/network/private-dns-zone/soa/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource SOA 'Microsoft.Network/privateDnsZones/SOA@2020-06-01' = { } resource SOA_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(SOA.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(SOA.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/soa/main.json b/avm/res/network/private-dns-zone/soa/main.json index 98736902a3..8f6068b730 100644 --- a/avm/res/network/private-dns-zone/soa/main.json +++ b/avm/res/network/private-dns-zone/soa/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17659308471739059293" + "version": "0.29.47.4906", + "templateHash": "6755707328778392735" }, "name": "Private DNS Zone SOA record", "description": "This module deploys a Private DNS Zone SOA record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "SOA_roleAssignments": { "copy": { "name": "SOA_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/srv/README.md b/avm/res/network/private-dns-zone/srv/README.md index 798a49ed1e..0a35914411 100644 --- a/avm/res/network/private-dns-zone/srv/README.md +++ b/avm/res/network/private-dns-zone/srv/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/srv/main.bicep b/avm/res/network/private-dns-zone/srv/main.bicep index 55e55a56d1..8d5f2c4115 100644 --- a/avm/res/network/private-dns-zone/srv/main.bicep +++ b/avm/res/network/private-dns-zone/srv/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource SRV 'Microsoft.Network/privateDnsZones/SRV@2020-06-01' = { } resource SRV_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(SRV.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(SRV.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/srv/main.json b/avm/res/network/private-dns-zone/srv/main.json index f2335729f7..8a115f2fa6 100644 --- a/avm/res/network/private-dns-zone/srv/main.json +++ b/avm/res/network/private-dns-zone/srv/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6384537155512618270" + "version": "0.29.47.4906", + "templateHash": "662436323695062210" }, "name": "Private DNS Zone SRV record", "description": "This module deploys a Private DNS Zone SRV record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "SRV_roleAssignments": { "copy": { "name": "SRV_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/tests/e2e/max/main.test.bicep b/avm/res/network/private-dns-zone/tests/e2e/max/main.test.bicep index 7126f0214b..006a3842b2 100644 --- a/avm/res/network/private-dns-zone/tests/e2e/max/main.test.bicep +++ b/avm/res/network/private-dns-zone/tests/e2e/max/main.test.bicep @@ -194,11 +194,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '8001f03c-2ca1-4dab-ab69-4dbaa3635af1' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/private-dns-zone/txt/README.md b/avm/res/network/private-dns-zone/txt/README.md index 23868a5bae..cb4d04a325 100644 --- a/avm/res/network/private-dns-zone/txt/README.md +++ b/avm/res/network/private-dns-zone/txt/README.md @@ -83,6 +83,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -133,6 +134,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-dns-zone/txt/main.bicep b/avm/res/network/private-dns-zone/txt/main.bicep index e472ee984b..01cdfa971d 100644 --- a/avm/res/network/private-dns-zone/txt/main.bicep +++ b/avm/res/network/private-dns-zone/txt/main.bicep @@ -42,6 +42,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { name: privateDnsZoneName } @@ -57,14 +68,10 @@ resource TXT 'Microsoft.Network/privateDnsZones/TXT@2020-06-01' = { } resource TXT_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(TXT.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(TXT.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -90,6 +97,9 @@ output resourceGroupName string = resourceGroup().name // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-dns-zone/txt/main.json b/avm/res/network/private-dns-zone/txt/main.json index 2825b366b9..da397eb782 100644 --- a/avm/res/network/private-dns-zone/txt/main.json +++ b/avm/res/network/private-dns-zone/txt/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14210083655409825976" + "version": "0.29.47.4906", + "templateHash": "356727884506799436" }, "name": "Private DNS Zone TXT record", "description": "This module deploys a Private DNS Zone TXT record.", @@ -18,6 +18,13 @@ "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": { @@ -122,6 +129,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "TXT_roleAssignments": { "copy": { "name": "TXT_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/private-dns-zone/version.json b/avm/res/network/private-dns-zone/version.json index 76049e1c4a..ea4f3b6e67 100644 --- a/avm/res/network/private-dns-zone/version.json +++ b/avm/res/network/private-dns-zone/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/private-dns-zone/virtual-network-link/main.json b/avm/res/network/private-dns-zone/virtual-network-link/main.json index 6f792bec63..4d6b151874 100644 --- a/avm/res/network/private-dns-zone/virtual-network-link/main.json +++ b/avm/res/network/private-dns-zone/virtual-network-link/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17374916809049988585" + "version": "0.29.47.4906", + "templateHash": "1713449351614683457" }, "name": "Private DNS Zone Virtual Network Link", "description": "This module deploys a Private DNS Zone Virtual Network Link.", diff --git a/avm/res/network/private-endpoint/README.md b/avm/res/network/private-endpoint/README.md index 87321bbb59..6cfdb25b50 100644 --- a/avm/res/network/private-endpoint/README.md +++ b/avm/res/network/private-endpoint/README.md @@ -153,10 +153,15 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = kind: 'CanNotDelete' name: 'myCustomLockName' } - privateDnsZoneGroupName: 'default' - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + name: 'default' + privateDnsZoneGroupConfigs: [ + { + name: 'config' + privateDnsZoneResourceId: '' + } + ] + } privateLinkServiceConnections: [ { name: 'npemax001' @@ -171,11 +176,13 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = ] roleAssignments: [ { + name: '6804f270-b4e9-455f-a11b-7f2a64e38f7c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -254,13 +261,16 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = "name": "myCustomLockName" } }, - "privateDnsZoneGroupName": { - "value": "default" - }, - "privateDnsZoneResourceIds": { - "value": [ - "" - ] + "privateDnsZoneGroup": { + "value": { + "name": "default", + "privateDnsZoneGroupConfigs": [ + { + "name": "config", + "privateDnsZoneResourceId": "" + } + ] + } }, "privateLinkServiceConnections": { "value": [ @@ -279,11 +289,13 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = "roleAssignments": { "value": [ { + "name": "6804f270-b4e9-455f-a11b-7f2a64e38f7c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -439,9 +451,13 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = kind: 'CanNotDelete' name: 'myCustomLockName' } - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } privateLinkServiceConnections: [ { name: 'npewaf001' @@ -511,10 +527,14 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = "name": "myCustomLockName" } }, - "privateDnsZoneResourceIds": { - "value": [ - "" - ] + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + } }, "privateLinkServiceConnections": { "value": [ @@ -565,8 +585,7 @@ module privateEndpoint 'br/public:avm/res/network/private-endpoint:' = | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`manualPrivateLinkServiceConnections`](#parameter-manualprivatelinkserviceconnections) | array | 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. | -| [`privateDnsZoneGroupName`](#parameter-privatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnections`](#parameter-privatelinkserviceconnections) | array | A grouping of information about the connection to the remote resource. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-tags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -798,20 +817,65 @@ A message passed to the owner of the remote resource with this connection reques - Required: Yes - Type: string -### Parameter: `privateDnsZoneGroupName` +### Parameter: `privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No -- Type: string +- Type: object -### Parameter: `privateDnsZoneResourceIds` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateDnsZoneGroup.privateDnsZoneGroupConfigs` The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. -- Required: No +- Required: Yes - Type: array +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + +### Parameter: `privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string + ### Parameter: `privateLinkServiceConnections` A grouping of information about the connection to the remote resource. @@ -896,6 +960,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -946,6 +1011,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -975,9 +1047,11 @@ Tags to be applied on all resources/resource groups in this deployment. | Output | Type | Description | | :-- | :-- | :-- | +| `customDnsConfig` | | The custom DNS configurations of the private endpoint. | | `groupId` | string | The group Id for the private endpoint Group. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the private endpoint. | +| `networkInterfaceIds` | array | The IDs of the network interfaces associated with the private endpoint. | | `resourceGroupName` | string | The resource group the private endpoint was deployed into. | | `resourceId` | string | The resource ID of the private endpoint. | diff --git a/avm/res/network/private-endpoint/main.bicep b/avm/res/network/private-endpoint/main.bicep index f893474088..f30b4f59f6 100644 --- a/avm/res/network/private-endpoint/main.bicep +++ b/avm/res/network/private-endpoint/main.bicep @@ -17,11 +17,8 @@ param customNetworkInterfaceName string? @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') param ipConfigurations ipConfigurationsType -@description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') -param privateDnsZoneGroupName string? - -@description('Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') -param privateDnsZoneResourceIds array? +@description('Optional. The private DNS zone group to configure for the private endpoint.') +param privateDnsZoneGroup privateDnsZoneGroupType? @description('Optional. Location for all Resources.') param location string = resourceGroup().location @@ -81,6 +78,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-privateendpoint.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -121,12 +129,12 @@ resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = { } } -module privateEndpoint_privateDnsZoneGroup 'private-dns-zone-group/main.bicep' = if (!empty(privateDnsZoneResourceIds)) { +module privateEndpoint_privateDnsZoneGroup 'private-dns-zone-group/main.bicep' = if (!empty(privateDnsZoneGroup)) { name: '${uniqueString(deployment().name)}-PrivateEndpoint-PrivateDnsZoneGroup' params: { - name: privateDnsZoneGroupName ?? 'default' - privateDNSResourceIds: privateDnsZoneResourceIds ?? [] + name: privateDnsZoneGroup.?name privateEndpointName: privateEndpoint.name + privateDnsZoneConfigs: privateDnsZoneGroup!.privateDnsZoneGroupConfigs } } @@ -142,14 +150,10 @@ resource privateEndpoint_lock 'Microsoft.Authorization/locks@2020-05-01' = if (! } resource privateEndpoint_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(privateEndpoint.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(privateEndpoint.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -173,6 +177,12 @@ output name string = privateEndpoint.name @description('The location the resource was deployed into.') output location string = privateEndpoint.location +@description('The custom DNS configurations of the private endpoint.') +output customDnsConfig customDnsConfigType = privateEndpoint.properties.customDnsConfigs + +@description('The IDs of the network interfaces associated with the private endpoint.') +output networkInterfaceIds array = privateEndpoint.properties.networkInterfaces + @description('The group Id for the private endpoint Group.') output groupId string = !empty(privateEndpoint.properties.manualPrivateLinkServiceConnections) && length(privateEndpoint.properties.manualPrivateLinkServiceConnections[0].properties.?groupIds) > 0 ? privateEndpoint.properties.manualPrivateLinkServiceConnections[0].properties.?groupIds[0] ?? '' @@ -184,7 +194,20 @@ output groupId string = !empty(privateEndpoint.properties.manualPrivateLinkServi // Definitions // // ================ // +import { privateDnsZoneGroupConfigType } from 'private-dns-zone-group/main.bicep' + +type privateDnsZoneGroupType = { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: privateDnsZoneGroupConfigType[] +} + type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-endpoint/main.json b/avm/res/network/private-endpoint/main.json index a76b7fb3e3..f5b0f0a552 100644 --- a/avm/res/network/private-endpoint/main.json +++ b/avm/res/network/private-endpoint/main.json @@ -5,19 +5,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12384550528617388749" + "version": "0.29.47.4906", + "templateHash": "890546444303772798" }, "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": { @@ -257,6 +285,29 @@ } }, "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": { @@ -292,18 +343,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -359,6 +403,13 @@ } }, "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')]", @@ -436,27 +487,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -467,28 +518,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.28.1.47646", - "templateHash": "18351493760118120087" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -496,12 +571,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -515,27 +593,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "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('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -595,6 +682,20 @@ }, "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": { diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/README.md b/avm/res/network/private-endpoint/private-dns-zone-group/README.md index 9cafeb7cf3..a8ab19677e 100644 --- a/avm/res/network/private-endpoint/private-dns-zone-group/README.md +++ b/avm/res/network/private-endpoint/private-dns-zone-group/README.md @@ -22,7 +22,7 @@ This module deploys a Private Endpoint Private DNS Zone Group. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`privateDNSResourceIds`](#parameter-privatednsresourceids) | array | Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneConfigs`](#parameter-privatednszoneconfigs) | array | Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones. | **Conditional parameters** @@ -36,13 +36,39 @@ This module deploys a Private Endpoint Private DNS Zone Group. | :-- | :-- | :-- | | [`name`](#parameter-name) | string | The name of the private DNS zone group. | -### Parameter: `privateDNSResourceIds` +### Parameter: `privateDnsZoneConfigs` -Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones. +Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones. - Required: Yes - Type: array +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privatednszoneconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privatednszoneconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateDnsZoneConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateDnsZoneConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + ### Parameter: `privateEndpointName` The name of the parent private endpoint. Required if the template is used in a standalone deployment. diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep b/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep index a2e7023024..959b131c0a 100644 --- a/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep +++ b/avm/res/network/private-endpoint/private-dns-zone-group/main.bicep @@ -5,19 +5,19 @@ metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment.') param privateEndpointName string -@description('Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones.') +@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.') @minLength(1) @maxLength(5) -param privateDNSResourceIds array +param privateDnsZoneConfigs privateDnsZoneGroupConfigType[] @description('Optional. The name of the private DNS zone group.') param name string = 'default' -var privateDnsZoneConfigs = [ - for privateDNSResourceId in privateDNSResourceIds: { - name: last(split(privateDNSResourceId, '/'))! +var privateDnsZoneConfigsVar = [ + for privateDnsZoneConfig in privateDnsZoneConfigs: { + name: privateDnsZoneConfig.?name ?? last(split(privateDnsZoneConfig.privateDnsZoneResourceId, '/')) properties: { - privateDnsZoneId: privateDNSResourceId + privateDnsZoneId: privateDnsZoneConfig.privateDnsZoneResourceId } } ] @@ -30,7 +30,7 @@ resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneG name: name parent: privateEndpoint properties: { - privateDnsZoneConfigs: privateDnsZoneConfigs + privateDnsZoneConfigs: privateDnsZoneConfigsVar } } @@ -42,3 +42,16 @@ output resourceId string = privateDnsZoneGroup.id @description('The resource group the private endpoint DNS zone group was deployed into.') output resourceGroupName string = resourceGroup().name + +// ================ // +// Definitions // +// ================ // + +@export() +type privateDnsZoneGroupConfigType = { + @description('Optional. The name of the private DNS zone group config.') + name: string? + + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string +} diff --git a/avm/res/network/private-endpoint/private-dns-zone-group/main.json b/avm/res/network/private-endpoint/private-dns-zone-group/main.json index 8661ce0a3d..c59c286088 100644 --- a/avm/res/network/private-endpoint/private-dns-zone-group/main.json +++ b/avm/res/network/private-endpoint/private-dns-zone-group/main.json @@ -1,16 +1,40 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18351493760118120087" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -18,12 +42,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -37,27 +64,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "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('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", diff --git a/avm/res/network/private-endpoint/tests/e2e/max/main.test.bicep b/avm/res/network/private-endpoint/tests/e2e/max/main.test.bicep index 7d708bd173..cfb7667b6f 100644 --- a/avm/res/network/private-endpoint/tests/e2e/max/main.test.bicep +++ b/avm/res/network/private-endpoint/tests/e2e/max/main.test.bicep @@ -63,16 +63,24 @@ module testDeployment '../../../main.bicep' = [ kind: 'CanNotDelete' name: 'myCustomLockName' } - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + name: 'default' + privateDnsZoneGroupConfigs: [ + { + name: 'config' + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } roleAssignments: [ { + name: '6804f270-b4e9-455f-a11b-7f2a64e38f7c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -113,7 +121,6 @@ module testDeployment '../../../main.bicep' = [ Environment: 'Non-Prod' Role: 'DeploymentValidation' } - privateDnsZoneGroupName: 'default' privateLinkServiceConnections: [ { name: '${namePrefix}${serviceShort}001' diff --git a/avm/res/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep index 1687c147bd..9522b63077 100644 --- a/avm/res/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/private-endpoint/tests/e2e/waf-aligned/main.test.bicep @@ -63,9 +63,13 @@ module testDeployment '../../../main.bicep' = [ kind: 'CanNotDelete' name: 'myCustomLockName' } - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } ipConfigurations: [ { name: 'myIPconfig' diff --git a/avm/res/network/private-endpoint/version.json b/avm/res/network/private-endpoint/version.json index 96236a61ba..35040975ae 100644 --- a/avm/res/network/private-endpoint/version.json +++ b/avm/res/network/private-endpoint/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/private-link-service/README.md b/avm/res/network/private-link-service/README.md index 2347f7d833..3c7c63fe07 100644 --- a/avm/res/network/private-link-service/README.md +++ b/avm/res/network/private-link-service/README.md @@ -163,11 +163,13 @@ module privateLinkService 'br/public:avm/res/network/private-link-service:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -258,11 +260,13 @@ module privateLinkService 'br/public:avm/res/network/private-link-service:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -584,6 +588,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -634,6 +639,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/private-link-service/main.bicep b/avm/res/network/private-link-service/main.bicep index 99e489497d..6b2e7bfc58 100644 --- a/avm/res/network/private-link-service/main.bicep +++ b/avm/res/network/private-link-service/main.bicep @@ -63,6 +63,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-privatelinkservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -109,14 +120,14 @@ resource privateLinkService_lock 'Microsoft.Authorization/locks@2020-05-01' = if } resource privateLinkService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(privateLinkService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + privateLinkService.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -153,6 +164,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/private-link-service/main.json b/avm/res/network/private-link-service/main.json index dae76cb99d..78e55e98ae 100644 --- a/avm/res/network/private-link-service/main.json +++ b/avm/res/network/private-link-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16164614215536831485" + "version": "0.29.47.4906", + "templateHash": "4865728079341128058" }, "name": "Private Link Services", "description": "This module deploys a Private Link Service.", @@ -43,6 +43,13 @@ "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": { @@ -194,6 +201,13 @@ } }, "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')]", @@ -258,20 +272,20 @@ "privateLinkService_roleAssignments": { "copy": { "name": "privateLinkService_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateLinkServices/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateLinkServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateLinkServices', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "privateLinkService" diff --git a/avm/res/network/private-link-service/tests/e2e/max/main.test.bicep b/avm/res/network/private-link-service/tests/e2e/max/main.test.bicep index 1efed5c5a3..390a50d21f 100644 --- a/avm/res/network/private-link-service/tests/e2e/max/main.test.bicep +++ b/avm/res/network/private-link-service/tests/e2e/max/main.test.bicep @@ -92,11 +92,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'fec82bb5-8552-4c4b-a3f6-65bdae54d7f4' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/private-link-service/version.json b/avm/res/network/private-link-service/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/network/private-link-service/version.json +++ b/avm/res/network/private-link-service/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/public-ip-address/README.md b/avm/res/network/public-ip-address/README.md index ded4ce7179..ec12070383 100644 --- a/avm/res/network/public-ip-address/README.md +++ b/avm/res/network/public-ip-address/README.md @@ -117,11 +117,13 @@ module publicIpAddress 'br/public:avm/res/network/public-ip-address:' = publicIpPrefixResourceId: '' roleAssignments: [ { + name: '902f366b-ba61-4eb6-aa3a-786d317f2dbc' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -203,11 +205,13 @@ module publicIpAddress 'br/public:avm/res/network/public-ip-address:' = "roleAssignments": { "value": [ { + "name": "902f366b-ba61-4eb6-aa3a-786d317f2dbc", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -824,6 +828,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -874,6 +879,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/public-ip-address/main.bicep b/avm/res/network/public-ip-address/main.bicep index 885c2d4184..de3e06e253 100644 --- a/avm/res/network/public-ip-address/main.bicep +++ b/avm/res/network/public-ip-address/main.bicep @@ -109,6 +109,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-publicipaddress.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -164,14 +175,10 @@ resource publicIpAddress_lock 'Microsoft.Authorization/locks@2020-05-01' = if (! } resource publicIpAddress_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(publicIpAddress.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(publicIpAddress.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -232,6 +239,9 @@ output location string = publicIpAddress.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/public-ip-address/main.json b/avm/res/network/public-ip-address/main.json index 6d892e7b82..006fbb7955 100644 --- a/avm/res/network/public-ip-address/main.json +++ b/avm/res/network/public-ip-address/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7461208970739069973" + "version": "0.29.47.4906", + "templateHash": "3941145312505752817" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -18,6 +18,13 @@ "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": { @@ -431,6 +438,13 @@ } }, "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')]", @@ -503,20 +517,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/public-ip-address/tests/e2e/max/main.test.bicep b/avm/res/network/public-ip-address/tests/e2e/max/main.test.bicep index 23d89eb8fe..daea40b73e 100644 --- a/avm/res/network/public-ip-address/tests/e2e/max/main.test.bicep +++ b/avm/res/network/public-ip-address/tests/e2e/max/main.test.bicep @@ -75,11 +75,13 @@ module testDeployment '../../../main.bicep' = [ publicIPAllocationMethod: 'Static' roleAssignments: [ { + name: '902f366b-ba61-4eb6-aa3a-786d317f2dbc' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/public-ip-address/version.json b/avm/res/network/public-ip-address/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/network/public-ip-address/version.json +++ b/avm/res/network/public-ip-address/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/public-ip-prefix/README.md b/avm/res/network/public-ip-prefix/README.md index 70e67da18f..49423e718a 100644 --- a/avm/res/network/public-ip-prefix/README.md +++ b/avm/res/network/public-ip-prefix/README.md @@ -107,11 +107,13 @@ module publicIpPrefix 'br/public:avm/res/network/public-ip-prefix:' = { } roleAssignments: [ { + name: 'bf62ed65-07be-48e8-b760-2d59795cd282' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -167,11 +169,13 @@ module publicIpPrefix 'br/public:avm/res/network/public-ip-prefix:' = { "roleAssignments": { "value": [ { + "name": "bf62ed65-07be-48e8-b760-2d59795cd282", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -385,6 +389,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -435,6 +440,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/public-ip-prefix/main.bicep b/avm/res/network/public-ip-prefix/main.bicep index 2992b22097..37b640a985 100644 --- a/avm/res/network/public-ip-prefix/main.bicep +++ b/avm/res/network/public-ip-prefix/main.bicep @@ -59,6 +59,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-publicipprefix.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -105,14 +116,10 @@ resource publicIpPrefix_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource publicIpPrefix_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(publicIpPrefix.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(publicIpPrefix.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -149,6 +156,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/public-ip-prefix/main.json b/avm/res/network/public-ip-prefix/main.json index c4e1d9d0a5..1faf94e7fa 100644 --- a/avm/res/network/public-ip-prefix/main.json +++ b/avm/res/network/public-ip-prefix/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14799608777680945977" + "version": "0.29.47.4906", + "templateHash": "14196668450539933413" }, "name": "Public IP Prefixes", "description": "This module deploys a Public IP Prefix.", @@ -43,6 +43,13 @@ "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": { @@ -182,6 +189,13 @@ } }, "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')]", @@ -245,20 +259,20 @@ "publicIpPrefix_roleAssignments": { "copy": { "name": "publicIpPrefix_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPPrefixes/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPPrefixes', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPPrefixes', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "publicIpPrefix" diff --git a/avm/res/network/public-ip-prefix/tests/e2e/max/main.test.bicep b/avm/res/network/public-ip-prefix/tests/e2e/max/main.test.bicep index 8cda1c80db..9e3d908bcb 100644 --- a/avm/res/network/public-ip-prefix/tests/e2e/max/main.test.bicep +++ b/avm/res/network/public-ip-prefix/tests/e2e/max/main.test.bicep @@ -59,11 +59,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'bf62ed65-07be-48e8-b760-2d59795cd282' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/public-ip-prefix/version.json b/avm/res/network/public-ip-prefix/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/network/public-ip-prefix/version.json +++ b/avm/res/network/public-ip-prefix/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/route-table/README.md b/avm/res/network/route-table/README.md index 753d31547a..00518a94e2 100644 --- a/avm/res/network/route-table/README.md +++ b/avm/res/network/route-table/README.md @@ -102,11 +102,13 @@ module routeTable 'br/public:avm/res/network/route-table:' = { } roleAssignments: [ { + name: 'de4b134c-7087-480d-892f-ce6629720d29' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -165,11 +167,13 @@ module routeTable 'br/public:avm/res/network/route-table:' = { "roleAssignments": { "value": [ { + "name": "de4b134c-7087-480d-892f-ce6629720d29", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -409,6 +413,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -459,6 +464,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/route-table/main.bicep b/avm/res/network/route-table/main.bicep index 6381f007e4..b4365c4edc 100644 --- a/avm/res/network/route-table/main.bicep +++ b/avm/res/network/route-table/main.bicep @@ -44,6 +44,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -88,14 +99,10 @@ resource routeTable_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty } resource routeTable_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(routeTable.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(routeTable.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -132,6 +139,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/route-table/main.json b/avm/res/network/route-table/main.json index b4d918e8a7..2e9fb6794d 100644 --- a/avm/res/network/route-table/main.json +++ b/avm/res/network/route-table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9367738957697121271" + "version": "0.29.47.4906", + "templateHash": "17607817156422907174" }, "name": "Route Tables", "description": "This module deploys a User Defined Route Table (UDR).", @@ -43,6 +43,13 @@ "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": { @@ -217,6 +224,13 @@ } }, "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')]", @@ -275,20 +289,20 @@ "routeTable_roleAssignments": { "copy": { "name": "routeTable_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/routeTables/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/routeTables', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/routeTables', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "routeTable" diff --git a/avm/res/network/route-table/tests/e2e/max/main.test.bicep b/avm/res/network/route-table/tests/e2e/max/main.test.bicep index 52bcc02ac8..b10d8ee6a1 100644 --- a/avm/res/network/route-table/tests/e2e/max/main.test.bicep +++ b/avm/res/network/route-table/tests/e2e/max/main.test.bicep @@ -56,11 +56,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: 'de4b134c-7087-480d-892f-ce6629720d29' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/route-table/version.json b/avm/res/network/route-table/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/route-table/version.json +++ b/avm/res/network/route-table/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/service-endpoint-policy/README.md b/avm/res/network/service-endpoint-policy/README.md index 7b81d1a239..3bd8384326 100644 --- a/avm/res/network/service-endpoint-policy/README.md +++ b/avm/res/network/service-endpoint-policy/README.md @@ -258,6 +258,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -308,6 +309,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/service-endpoint-policy/main.bicep b/avm/res/network/service-endpoint-policy/main.bicep index d8cb7faa3b..e4c4530f09 100644 --- a/avm/res/network/service-endpoint-policy/main.bicep +++ b/avm/res/network/service-endpoint-policy/main.bicep @@ -49,6 +49,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -95,14 +106,14 @@ resource serviceEndpointPolicy_lock 'Microsoft.Authorization/locks@2020-05-01' = } resource serviceEndpointPolicy_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(serviceEndpointPolicy.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + serviceEndpointPolicy.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -131,6 +142,9 @@ type lockType = { // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/service-endpoint-policy/main.json b/avm/res/network/service-endpoint-policy/main.json index 27c9b3cf3e..b8d6b768bd 100644 --- a/avm/res/network/service-endpoint-policy/main.json +++ b/avm/res/network/service-endpoint-policy/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7675130901596316058" + "version": "0.29.47.4906", + "templateHash": "10068025317868682479" }, "name": "Service-Endpoint-Policy", "description": "This module deploys a Service Endpoint Policy.", @@ -43,6 +43,13 @@ "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": { @@ -170,6 +177,13 @@ } }, "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')]", @@ -229,20 +243,20 @@ "serviceEndpointPolicy_roleAssignments": { "copy": { "name": "serviceEndpointPolicy_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/serviceEndpointPolicies/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/serviceEndpointPolicies', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/serviceEndpointPolicies', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "serviceEndpointPolicy" diff --git a/avm/res/network/service-endpoint-policy/version.json b/avm/res/network/service-endpoint-policy/version.json index 1c884ecaa9..729ac87673 100644 --- a/avm/res/network/service-endpoint-policy/version.json +++ b/avm/res/network/service-endpoint-policy/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/trafficmanagerprofile/README.md b/avm/res/network/trafficmanagerprofile/README.md index 4b87c3289c..75c7970297 100644 --- a/avm/res/network/trafficmanagerprofile/README.md +++ b/avm/res/network/trafficmanagerprofile/README.md @@ -118,11 +118,13 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -190,11 +192,13 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -689,6 +693,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -739,6 +744,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/trafficmanagerprofile/main.bicep b/avm/res/network/trafficmanagerprofile/main.bicep index 0bebb3880b..69746f5624 100644 --- a/avm/res/network/trafficmanagerprofile/main.bicep +++ b/avm/res/network/trafficmanagerprofile/main.bicep @@ -90,6 +90,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-trafficmanagerprofile.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -168,14 +179,14 @@ resource trafficManagerProfile_diagnosticSettings 'Microsoft.Insights/diagnostic ] resource trafficManagerProfile_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(trafficManagerProfile.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + trafficManagerProfile.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -209,6 +220,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/trafficmanagerprofile/main.json b/avm/res/network/trafficmanagerprofile/main.json index 8b877523bb..275f4d0955 100644 --- a/avm/res/network/trafficmanagerprofile/main.json +++ b/avm/res/network/trafficmanagerprofile/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15010830605134609047" + "version": "0.29.47.4906", + "templateHash": "2090813965996228671" }, "name": "Traffic Manager Profiles", "description": "This module deploys a Traffic Manager Profile.", @@ -43,6 +43,13 @@ "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": { @@ -350,6 +357,13 @@ } }, "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')]", @@ -458,20 +472,20 @@ "trafficManagerProfile_roleAssignments": { "copy": { "name": "trafficManagerProfile_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/trafficmanagerprofiles/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/trafficmanagerprofiles', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/trafficmanagerprofiles', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "trafficManagerProfile" diff --git a/avm/res/network/trafficmanagerprofile/tests/e2e/max/main.test.bicep b/avm/res/network/trafficmanagerprofile/tests/e2e/max/main.test.bicep index b756d168b8..2f2120ccf1 100644 --- a/avm/res/network/trafficmanagerprofile/tests/e2e/max/main.test.bicep +++ b/avm/res/network/trafficmanagerprofile/tests/e2e/max/main.test.bicep @@ -86,11 +86,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '76e7bd82-b689-4072-87be-519bfabf733e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/trafficmanagerprofile/version.json b/avm/res/network/trafficmanagerprofile/version.json index 0200aa0775..729ac87673 100644 --- a/avm/res/network/trafficmanagerprofile/version.json +++ b/avm/res/network/trafficmanagerprofile/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ - "./main.json" + "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/virtual-hub/README.md b/avm/res/network/virtual-hub/README.md index 2c7b689a6a..78f0828fa9 100644 --- a/avm/res/network/virtual-hub/README.md +++ b/avm/res/network/virtual-hub/README.md @@ -10,6 +10,7 @@ If you are planning to deploy a Secure Virtual Hub (with an Azure Firewall integ - [Parameters](#Parameters) - [Outputs](#Outputs) - [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) - [Data Collection](#Data-Collection) ## Resource Types @@ -19,7 +20,8 @@ If you are planning to deploy a Secure Virtual Hub (with an Azure Firewall integ | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Network/virtualHubs` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualHubs) | | `Microsoft.Network/virtualHubs/hubRouteTables` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/virtualHubs/hubRouteTables) | -| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/virtualHubs/hubVirtualNetworkConnections) | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections) | +| `Microsoft.Network/virtualHubs/routingIntent` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualHubs/routingIntent) | ## Usage examples @@ -31,7 +33,8 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) -- [WAF-aligned](#example-3-waf-aligned) +- [Using Routing Intent](#example-3-using-routing-intent) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -223,7 +226,121 @@ module virtualHub 'br/public:avm/res/network/virtual-hub:' = {

-### Example 3: _WAF-aligned_ +### Example 3: _Using Routing Intent_ + +This instance deploys the module the Virtual WAN hub with Routing Intent enabled; requires an existing Virtual Hub, as well the firewall Resource ID. + + +

+ +via Bicep module + +```bicep +module virtualHub 'br/public:avm/res/network/virtual-hub:' = { + name: 'virtualHubDeployment' + params: { + // Required parameters + addressPrefix: '10.10.0.0/23' + name: 'nvhrtint' + virtualWanId: '' + // Non-required parameters + azureFirewallResourceId: '' + hubRouteTables: [] + hubRoutingPreference: 'ASPath' + hubVirtualNetworkConnections: [ + { + name: 'connection1' + remoteVirtualNetworkId: '' + routingConfiguration: {} + } + ] + internetToFirewall: false + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + privateToFirewall: true + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "addressPrefix": { + "value": "10.10.0.0/23" + }, + "name": { + "value": "nvhrtint" + }, + "virtualWanId": { + "value": "" + }, + // Non-required parameters + "azureFirewallResourceId": { + "value": "" + }, + "hubRouteTables": { + "value": [] + }, + "hubRoutingPreference": { + "value": "ASPath" + }, + "hubVirtualNetworkConnections": { + "value": [ + { + "name": "connection1", + "remoteVirtualNetworkId": "", + "routingConfiguration": {} + } + ] + }, + "internetToFirewall": { + "value": false + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "privateToFirewall": { + "value": true + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -379,10 +496,12 @@ module virtualHub 'br/public:avm/res/network/virtual-hub:' = { | [`hubRouteTables`](#parameter-hubroutetables) | array | Route tables to create for the virtual hub. | | [`hubRoutingPreference`](#parameter-hubroutingpreference) | string | The preferred routing preference for this virtual hub. | | [`hubVirtualNetworkConnections`](#parameter-hubvirtualnetworkconnections) | array | Virtual network connections to create for the virtual hub. | +| [`internetToFirewall`](#parameter-internettofirewall) | bool | Configures Routing Intent to forward Internet traffic (0.0.0.0/0) to Azure Firewall. Default is true. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`p2SVpnGatewayId`](#parameter-p2svpngatewayid) | string | Resource ID of the Point-to-Site VPN Gateway to link to. | | [`preferredRoutingGateway`](#parameter-preferredroutinggateway) | string | The preferred routing gateway types. | +| [`privateToFirewall`](#parameter-privatetofirewall) | bool | Configures Routing Intent to forward Private traffic (RFC 1918) to Azure Firewall. Default is true. | | [`routeTableRoutes`](#parameter-routetableroutes) | array | VirtualHub route tables. | | [`securityPartnerProviderId`](#parameter-securitypartnerproviderid) | string | ID of the Security Partner Provider to link to. | | [`securityProviderName`](#parameter-securityprovidername) | string | The Security Provider name. | @@ -479,6 +598,14 @@ Virtual network connections to create for the virtual hub. - Type: array - Default: `[]` +### Parameter: `internetToFirewall` + +Configures Routing Intent to forward Internet traffic (0.0.0.0/0) to Azure Firewall. Default is true. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `location` Location for all resources. @@ -548,6 +675,14 @@ The preferred routing gateway types. ] ``` +### Parameter: `privateToFirewall` + +Configures Routing Intent to forward Private traffic (RFC 1918) to Azure Firewall. Default is true. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `routeTableRoutes` VirtualHub route tables. @@ -639,6 +774,12 @@ Resource ID of the VPN Gateway to link to. _None_ +## Notes + +**Configuring Routing Intent** + +Due to limitations with the virtual hub resource provider, to fully enable routing intent this resource will need to be invoked twice within the virtual WAN pattern. The first invocation will create the Virtual Hub, after which the resource creating the firewall can be called; then the second invocation can configure routing intent. This ensures that the resources are created in the correct order, and that the required resource ID parameters are available when they are needed. + ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/virtual-hub/hub-route-table/main.json b/avm/res/network/virtual-hub/hub-route-table/main.json index d84fdbe0d2..574eb2230e 100644 --- a/avm/res/network/virtual-hub/hub-route-table/main.json +++ b/avm/res/network/virtual-hub/hub-route-table/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7178717349327479453" + "version": "0.29.47.4906", + "templateHash": "2767725465951482406" }, "name": "Virtual Hub Route Tables", "description": "This module deploys a Virtual Hub Route Table.", diff --git a/avm/res/network/virtual-hub/hub-routing-intent/README.md b/avm/res/network/virtual-hub/hub-routing-intent/README.md new file mode 100644 index 0000000000..f26802847e --- /dev/null +++ b/avm/res/network/virtual-hub/hub-routing-intent/README.md @@ -0,0 +1,84 @@ +# Virtual Hub Routing Intent `[Microsoft.Network/virtualHubs]` + +This module configures Routing Intent for a Virtual Hub; this module requires an existing Virtual Hub, as well the firewall Resource ID. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualHubs/routingIntent` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualHubs/routingIntent) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`azureFirewallResourceId`](#parameter-azurefirewallresourceid) | string | Hub firewall Resource ID. | +| [`internetToFirewall`](#parameter-internettofirewall) | bool | Configures Routing Intent to Forward Internet traffic to the firewall (0.0.0.0/0). | +| [`privateToFirewall`](#parameter-privatetofirewall) | bool | Configures Routing Intent to forward Private traffic to the firewall (RFC1918). | +| [`virtualHubName`](#parameter-virtualhubname) | string | Name of the Virtual Hub. | + +### Parameter: `azureFirewallResourceId` + +Hub firewall Resource ID. + +- Required: Yes +- Type: string + +### Parameter: `internetToFirewall` + +Configures Routing Intent to Forward Internet traffic to the firewall (0.0.0.0/0). + +- Required: Yes +- Type: bool + +### Parameter: `privateToFirewall` + +Configures Routing Intent to forward Private traffic to the firewall (RFC1918). + +- Required: Yes +- Type: bool + +### Parameter: `virtualHubName` + +Name of the Virtual Hub. + +- Required: Yes +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Routing Intent configuration. | +| `resourceGroupName` | string | The resource group the Routing Intent configuration was deployed into. | +| `resourceId` | string | The resource ID of the Routing Intent configuration. | + +## Cross-referenced modules + +_None_ + +## Notes + +The use of Routing Intent in a Virtual Hub requires the hub be made 'secure' by associating a firewall (to serve as the next-hop for the routing policy). + +The 'azureFirewall' parameter in Microsoft.Network/virtualHubs is *read-only*; in order to properly deploy a Virtual Hub using Routing Intent, the hub's Resource ID must first be passed to the firewall in order for it to be properly associated. In order for this resource to work properly, the resources need to be created in the following order: + +- **Virtual Hub**: Suggest minimal hub configuration; name/location/addressPrefix/virtualWan.id +- **Firewall**: Including Virtual Hub Resource ID in the firewall configuration +- **Virtual Hub**: Including all remaining parameters + Routing Intent + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/virtual-hub/hub-routing-intent/main.bicep b/avm/res/network/virtual-hub/hub-routing-intent/main.bicep new file mode 100644 index 0000000000..aed6123d50 --- /dev/null +++ b/avm/res/network/virtual-hub/hub-routing-intent/main.bicep @@ -0,0 +1,68 @@ +metadata name = 'Virtual Hub Routing Intent' +metadata description = 'This module configures Routing Intent for a Virtual Hub; this module requires an existing Virtual Hub, as well the firewall Resource ID.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Hub firewall Resource ID.') +param azureFirewallResourceId string + +@description('Required. Name of the Virtual Hub.') +param virtualHubName string + +@description('Required. Configures Routing Intent to forward Private traffic to the firewall (RFC1918).') +param privateToFirewall bool + +@description('Required. Configures Routing Intent to Forward Internet traffic to the firewall (0.0.0.0/0).') +param internetToFirewall bool + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' existing = { + name: virtualHubName +} + +resource routingIntent 'Microsoft.Network/virtualHubs/routingIntent@2023-11-01' = { + name: 'defaultRouteTable' + parent : virtualHub + properties: { + routingPolicies: (internetToFirewall == true && privateToFirewall == true) ? [ + { + name: '_policy_PublicTraffic' + destinations: [ + 'Internet' + ] + nextHop: azureFirewallResourceId + } + { + name: '_policy_PrivateTraffic' + destinations: [ + 'PrivateTraffic' + ] + nextHop: azureFirewallResourceId + } + ] : (internetToFirewall == true && privateToFirewall == false) ? [ + { + name: '_policy_PublicTraffic' + destinations: [ + 'Internet' + ] + nextHop: azureFirewallResourceId + } + ] : (internetToFirewall == false && privateToFirewall == true) ? [ + { + name: '_policy_PrivateTraffic' + destinations: [ + 'PrivateTraffic' + ] + nextHop: azureFirewallResourceId + } + ] + : null + } +} + +@description('The name of the Routing Intent configuration.') +output name string = routingIntent.name + +@description('The resource ID of the Routing Intent configuration.') +output resourceId string = routingIntent.id + +@description('The resource group the Routing Intent configuration was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/network/virtual-hub/hub-routing-intent/main.json b/avm/res/network/virtual-hub/hub-routing-intent/main.json new file mode 100644 index 0000000000..f540aae020 --- /dev/null +++ b/avm/res/network/virtual-hub/hub-routing-intent/main.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9080948373192033748" + }, + "name": "Virtual Hub Routing Intent", + "description": "This module configures Routing Intent for a Virtual Hub; this module requires an existing Virtual Hub, as well the firewall Resource ID.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "azureFirewallResourceId": { + "type": "string", + "metadata": { + "description": "Required. Hub firewall Resource ID." + } + }, + "virtualHubName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Virtual Hub." + } + }, + "privateToFirewall": { + "type": "bool", + "metadata": { + "description": "Required. Configures Routing Intent to forward Private traffic to the firewall (RFC1918)." + } + }, + "internetToFirewall": { + "type": "bool", + "metadata": { + "description": "Required. Configures Routing Intent to Forward Internet traffic to the firewall (0.0.0.0/0)." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualHubs/routingIntent", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('virtualHubName'), 'defaultRouteTable')]", + "properties": { + "routingPolicies": "[if(and(equals(parameters('internetToFirewall'), true()), equals(parameters('privateToFirewall'), true())), createArray(createObject('name', '_policy_PublicTraffic', 'destinations', createArray('Internet'), 'nextHop', parameters('azureFirewallResourceId')), createObject('name', '_policy_PrivateTraffic', 'destinations', createArray('PrivateTraffic'), 'nextHop', parameters('azureFirewallResourceId'))), if(and(equals(parameters('internetToFirewall'), true()), equals(parameters('privateToFirewall'), false())), createArray(createObject('name', '_policy_PublicTraffic', 'destinations', createArray('Internet'), 'nextHop', parameters('azureFirewallResourceId'))), if(and(equals(parameters('internetToFirewall'), false()), equals(parameters('privateToFirewall'), true())), createArray(createObject('name', '_policy_PrivateTraffic', 'destinations', createArray('PrivateTraffic'), 'nextHop', parameters('azureFirewallResourceId'))), null())))]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Routing Intent configuration." + }, + "value": "defaultRouteTable" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Routing Intent configuration." + }, + "value": "[resourceId('Microsoft.Network/virtualHubs/routingIntent', parameters('virtualHubName'), 'defaultRouteTable')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Routing Intent configuration was deployed into." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md b/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md index 43d153f7a7..41874c8b1e 100644 --- a/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md +++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/README.md @@ -14,7 +14,7 @@ This module deploys a Virtual Hub Virtual Network Connection. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/virtualHubs/hubVirtualNetworkConnections) | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections) | ## Parameters diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep index f9751898ec..0dadaaf142 100644 --- a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep +++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.bicep @@ -17,11 +17,11 @@ param remoteVirtualNetworkId string @description('Optional. Routing Configuration indicating the associated and propagated route tables for this connection.') param routingConfiguration object = {} -resource virtualHub 'Microsoft.Network/virtualHubs@2022-11-01' existing = { +resource virtualHub 'Microsoft.Network/virtualHubs@2024-01-01' existing = { name: virtualHubName } -resource hubVirtualNetworkConnection 'Microsoft.Network/virtualHubs/hubVirtualNetworkConnections@2022-11-01' = { +resource hubVirtualNetworkConnection 'Microsoft.Network/virtualHubs/hubVirtualNetworkConnections@2024-01-01' = { name: name parent: virtualHub properties: { diff --git a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json index 83f863e837..ac1b70d82e 100644 --- a/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json +++ b/avm/res/network/virtual-hub/hub-virtual-network-connection/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3505452198428635330" + "version": "0.29.47.4906", + "templateHash": "12641692391238745355" }, "name": "Virtual Hub Virtual Network Connections", "description": "This module deploys a Virtual Hub Virtual Network Connection.", @@ -48,7 +48,7 @@ "resources": [ { "type": "Microsoft.Network/virtualHubs/hubVirtualNetworkConnections", - "apiVersion": "2022-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualHubName'), parameters('name'))]", "properties": { "enableInternetSecurity": "[parameters('enableInternetSecurity')]", diff --git a/avm/res/network/virtual-hub/main.bicep b/avm/res/network/virtual-hub/main.bicep index ae570f0f50..c21158f265 100644 --- a/avm/res/network/virtual-hub/main.bicep +++ b/avm/res/network/virtual-hub/main.bicep @@ -76,6 +76,12 @@ param virtualWanId string @description('Optional. Resource ID of the VPN Gateway to link to.') param vpnGatewayId string = '' +@description('Optional. Configures Routing Intent to forward Internet traffic (0.0.0.0/0) to Azure Firewall. Default is true.') +param internetToFirewall bool = true + +@description('Optional. Configures Routing Intent to forward Private traffic (RFC 1918) to Azure Firewall. Default is true.') +param privateToFirewall bool = true + @description('Optional. Route tables to create for the virtual hub.') param hubRouteTables array = [] @@ -171,8 +177,18 @@ resource virtualHub_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty scope: virtualHub } +module virtualHub_routingIntent 'hub-routing-intent/main.bicep' = if (!empty(azureFirewallResourceId) && (internetToFirewall || privateToFirewall)) { + name: '${uniqueString(deployment().name, location)}-routingIntent' + params: { + virtualHubName: virtualHub.name + azureFirewallResourceId: azureFirewallResourceId + internetToFirewall: internetToFirewall + privateToFirewall: privateToFirewall + } +} + module virtualHub_routeTables 'hub-route-table/main.bicep' = [ - for (routeTable, index) in hubRouteTables: { + for (routeTable, index) in (hubRouteTables ?? []): { name: '${uniqueString(deployment().name, location)}-routeTable-${index}' params: { virtualHubName: virtualHub.name diff --git a/avm/res/network/virtual-hub/main.json b/avm/res/network/virtual-hub/main.json index 5404500f8e..036e31e882 100644 --- a/avm/res/network/virtual-hub/main.json +++ b/avm/res/network/virtual-hub/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13594058281073706360" + "version": "0.29.47.4906", + "templateHash": "14437175207078546495" }, "name": "Virtual Hubs", "description": "This module deploys a Virtual Hub.\nIf you are planning to deploy a Secure Virtual Hub (with an Azure Firewall integrated), please refer to the Azure Firewall module.", @@ -186,6 +186,20 @@ "description": "Optional. Resource ID of the VPN Gateway to link to." } }, + "internetToFirewall": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Configures Routing Intent to forward Internet traffic (0.0.0.0/0) to Azure Firewall. Default is true." + } + }, + "privateToFirewall": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Configures Routing Intent to forward Private traffic (RFC 1918) to Azure Firewall. Default is true." + } + }, "hubRouteTables": { "type": "array", "defaultValue": [], @@ -276,10 +290,112 @@ "virtualHub" ] }, + "virtualHub_routingIntent": { + "condition": "[and(not(empty(parameters('azureFirewallResourceId'))), or(parameters('internetToFirewall'), parameters('privateToFirewall')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-routingIntent', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualHubName": { + "value": "[parameters('name')]" + }, + "azureFirewallResourceId": { + "value": "[parameters('azureFirewallResourceId')]" + }, + "internetToFirewall": { + "value": "[parameters('internetToFirewall')]" + }, + "privateToFirewall": { + "value": "[parameters('privateToFirewall')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9080948373192033748" + }, + "name": "Virtual Hub Routing Intent", + "description": "This module configures Routing Intent for a Virtual Hub; this module requires an existing Virtual Hub, as well the firewall Resource ID.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "azureFirewallResourceId": { + "type": "string", + "metadata": { + "description": "Required. Hub firewall Resource ID." + } + }, + "virtualHubName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Virtual Hub." + } + }, + "privateToFirewall": { + "type": "bool", + "metadata": { + "description": "Required. Configures Routing Intent to forward Private traffic to the firewall (RFC1918)." + } + }, + "internetToFirewall": { + "type": "bool", + "metadata": { + "description": "Required. Configures Routing Intent to Forward Internet traffic to the firewall (0.0.0.0/0)." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualHubs/routingIntent", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('virtualHubName'), 'defaultRouteTable')]", + "properties": { + "routingPolicies": "[if(and(equals(parameters('internetToFirewall'), true()), equals(parameters('privateToFirewall'), true())), createArray(createObject('name', '_policy_PublicTraffic', 'destinations', createArray('Internet'), 'nextHop', parameters('azureFirewallResourceId')), createObject('name', '_policy_PrivateTraffic', 'destinations', createArray('PrivateTraffic'), 'nextHop', parameters('azureFirewallResourceId'))), if(and(equals(parameters('internetToFirewall'), true()), equals(parameters('privateToFirewall'), false())), createArray(createObject('name', '_policy_PublicTraffic', 'destinations', createArray('Internet'), 'nextHop', parameters('azureFirewallResourceId'))), if(and(equals(parameters('internetToFirewall'), false()), equals(parameters('privateToFirewall'), true())), createArray(createObject('name', '_policy_PrivateTraffic', 'destinations', createArray('PrivateTraffic'), 'nextHop', parameters('azureFirewallResourceId'))), null())))]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Routing Intent configuration." + }, + "value": "defaultRouteTable" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Routing Intent configuration." + }, + "value": "[resourceId('Microsoft.Network/virtualHubs/routingIntent', parameters('virtualHubName'), 'defaultRouteTable')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Routing Intent configuration was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "virtualHub" + ] + }, "virtualHub_routeTables": { "copy": { "name": "virtualHub_routeTables", - "count": "[length(parameters('hubRouteTables'))]" + "count": "[length(coalesce(parameters('hubRouteTables'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -294,10 +410,10 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('hubRouteTables')[copyIndex()].name]" + "value": "[coalesce(parameters('hubRouteTables'), createArray())[copyIndex()].name]" }, - "labels": "[if(contains(parameters('hubRouteTables')[copyIndex()], 'labels'), createObject('value', parameters('hubRouteTables')[copyIndex()].labels), createObject('value', createArray()))]", - "routes": "[if(contains(parameters('hubRouteTables')[copyIndex()], 'routes'), createObject('value', parameters('hubRouteTables')[copyIndex()].routes), createObject('value', createArray()))]" + "labels": "[if(contains(coalesce(parameters('hubRouteTables'), createArray())[copyIndex()], 'labels'), createObject('value', coalesce(parameters('hubRouteTables'), createArray())[copyIndex()].labels), createObject('value', createArray()))]", + "routes": "[if(contains(coalesce(parameters('hubRouteTables'), createArray())[copyIndex()], 'routes'), createObject('value', coalesce(parameters('hubRouteTables'), createArray())[copyIndex()].routes), createObject('value', createArray()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -305,8 +421,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7178717349327479453" + "version": "0.29.47.4906", + "templateHash": "2767725465951482406" }, "name": "Virtual Hub Route Tables", "description": "This module deploys a Virtual Hub Route Table.", @@ -412,8 +528,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3505452198428635330" + "version": "0.29.47.4906", + "templateHash": "12641692391238745355" }, "name": "Virtual Hub Virtual Network Connections", "description": "This module deploys a Virtual Hub Virtual Network Connection.", @@ -456,7 +572,7 @@ "resources": [ { "type": "Microsoft.Network/virtualHubs/hubVirtualNetworkConnections", - "apiVersion": "2022-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualHubName'), parameters('name'))]", "properties": { "enableInternetSecurity": "[parameters('enableInternetSecurity')]", diff --git a/avm/res/network/virtual-hub/tests/e2e/routing-intent/dependencies.bicep b/avm/res/network/virtual-hub/tests/e2e/routing-intent/dependencies.bicep new file mode 100644 index 0000000000..39fc4c0351 --- /dev/null +++ b/avm/res/network/virtual-hub/tests/e2e/routing-intent/dependencies.bicep @@ -0,0 +1,81 @@ +@description('Required. The name of the Virtual WAN to create.') +param virtualWANName string + +@description('Required. The name of the Virtual Hub to create.') +param virtualHubName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Azure Firewall to create.') +param azureFirewallName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' + +resource virtualWan 'Microsoft.Network/virtualWans@2023-04-01' = { + name: virtualWANName + location: location +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2023-11-01' = { + name: virtualHubName + location: location + properties: { + addressPrefix: '10.10.0.0/23' + virtualWan: { + id: virtualWan.id + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource azureFirewall 'Microsoft.Network/azureFirewalls@2023-04-01' = { + name: azureFirewallName + location: location + properties: { + sku: { + name: 'AZFW_Hub' + tier: 'Premium' + } + hubIPAddresses: { + publicIPs: { + count: 1 + } + } + virtualHub: { + id: virtualHub.id + } + } +} + + +@description('The resource ID of the created Virtual WAN.') +output virtualWANResourceId string = virtualWan.id + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The resource ID of the created Azure Firewall') +output azureFirewallResourceId string = azureFirewall.id diff --git a/avm/res/network/virtual-hub/tests/e2e/routing-intent/main.test.bicep b/avm/res/network/virtual-hub/tests/e2e/routing-intent/main.test.bicep new file mode 100644 index 0000000000..737caa3be1 --- /dev/null +++ b/avm/res/network/virtual-hub/tests/e2e/routing-intent/main.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +metadata name = 'Using Routing Intent' +metadata description = 'This instance deploys the module the Virtual WAN hub with Routing Intent enabled; requires an existing Virtual Hub, as well the firewall Resource ID.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualHub-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvhrtint' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualWANName: 'dep-${namePrefix}-vw-${serviceShort}' + virtualHubName: '${namePrefix}-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + azureFirewallName: 'dep-${namePrefix}-azfw-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + dependsOn: [nestedDependencies] + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + addressPrefix: '10.10.0.0/23' + virtualWanId: nestedDependencies.outputs.virtualWANResourceId + hubRouteTables: [] + hubVirtualNetworkConnections: [ + { + name: 'connection1' + remoteVirtualNetworkId: nestedDependencies.outputs.virtualNetworkResourceId + routingConfiguration: {} + } + ] + hubRoutingPreference: 'ASPath' + azureFirewallResourceId: nestedDependencies.outputs.azureFirewallResourceId + internetToFirewall: false + privateToFirewall: true + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/virtual-network-gateway/README.md b/avm/res/network/virtual-network-gateway/README.md index a28285a5d1..ebd327fc96 100644 --- a/avm/res/network/virtual-network-gateway/README.md +++ b/avm/res/network/virtual-network-gateway/README.md @@ -18,7 +18,7 @@ This module deploys a Virtual Network Gateway. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/publicIPAddresses` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/publicIPAddresses) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | | `Microsoft.Network/virtualNetworkGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworkGateways) | | `Microsoft.Network/virtualNetworkGateways/natRules` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworkGateways/natRules) | @@ -394,11 +394,13 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: ] roleAssignments: [ { + name: 'db30550e-70b7-4dbe-901e-e9363b69c05f' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -540,11 +542,13 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "roleAssignments": { "value": [ { + "name": "db30550e-70b7-4dbe-901e-e9363b69c05f", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1595,6 +1599,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1645,6 +1650,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1733,7 +1745,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/public-ip-address:0.2.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.5.1` | Remote reference | ## Data Collection diff --git a/avm/res/network/virtual-network-gateway/main.bicep b/avm/res/network/virtual-network-gateway/main.bicep index 22a918c807..dd0d076ba8 100644 --- a/avm/res/network/virtual-network-gateway/main.bicep +++ b/avm/res/network/virtual-network-gateway/main.bicep @@ -138,8 +138,6 @@ param vpnClientAadConfiguration object = {} // ================// // Other Variables -var zones = [for zone in publicIpZones: string(zone)] - var gatewayPipAllocationMethod = skuName == 'Basic' ? 'Dynamic' : 'Static' var isActiveActiveValid = gatewayType != 'ExpressRoute' ? activeActive : false @@ -263,6 +261,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ================// // Deployments // // ================// @@ -291,7 +300,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT // Public IPs @batchSize(1) -module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.2.0' = [ +module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.5.1' = [ for (virtualGatewayPublicIpName, index) in virtualGatewayPipNameVar: { name: virtualGatewayPublicIpName params: { @@ -303,7 +312,7 @@ module publicIPAddress 'br/public:avm/res/network/public-ip-address:0.2.0' = [ publicIpPrefixResourceId: !empty(publicIPPrefixResourceId) ? publicIPPrefixResourceId : '' tags: tags skuName: skuName == 'Basic' ? 'Basic' : 'Standard' - zones: skuName != 'Basic' ? zones : [] + zones: skuName != 'Basic' ? publicIpZones : [] dnsSettings: { domainNameLabel: length(virtualGatewayPipNameVar) == length(domainNameLabel) ? domainNameLabel[index] @@ -406,14 +415,14 @@ resource virtualNetworkGateway_diagnosticSettings 'Microsoft.Insights/diagnostic ] resource virtualNetworkGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(virtualNetworkGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + virtualNetworkGateway.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -456,6 +465,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/virtual-network-gateway/main.json b/avm/res/network/virtual-network-gateway/main.json index f2e013ae35..c270f32631 100644 --- a/avm/res/network/virtual-network-gateway/main.json +++ b/avm/res/network/virtual-network-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13756500824845705848" + "version": "0.29.47.4906", + "templateHash": "17741491171223900170" }, "name": "Virtual Network Gateways", "description": "This module deploys a Virtual Network Gateway.", @@ -43,6 +43,13 @@ "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": { @@ -485,9 +492,9 @@ "variables": { "copy": [ { - "name": "zones", - "count": "[length(parameters('publicIpZones'))]", - "input": "[string(parameters('publicIpZones')[copyIndex('zones')])]" + "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)))))]" } ], "gatewayPipAllocationMethod": "[if(equals(parameters('skuName'), 'Basic'), 'Dynamic', 'Static')]", @@ -619,20 +626,20 @@ "virtualNetworkGateway_roleAssignments": { "copy": { "name": "virtualNetworkGateway_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworkGateways/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworkGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworkGateways', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "virtualNetworkGateway" @@ -674,7 +681,7 @@ "value": "[parameters('tags')]" }, "skuName": "[if(equals(parameters('skuName'), 'Basic'), createObject('value', 'Basic'), createObject('value', 'Standard'))]", - "zones": "[if(not(equals(parameters('skuName'), 'Basic')), createObject('value', variables('zones')), createObject('value', createArray()))]", + "zones": "[if(not(equals(parameters('skuName'), 'Basic')), createObject('value', parameters('publicIpZones')), createObject('value', createArray()))]", "dnsSettings": { "value": { "domainNameLabel": "[if(equals(length(variables('virtualGatewayPipNameVar')), length(parameters('domainNameLabel'))), parameters('domainNameLabel')[copyIndex()], variables('virtualGatewayPipNameVar')[copyIndex()])]", @@ -689,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "11486686724612026983" + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -702,10 +709,17 @@ "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 name of the role to assign. If it cannot be found you can specify the role definition ID instead." + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." } }, "principalId": { @@ -739,7 +753,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, "conditionVersion": { @@ -833,11 +847,15 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } } }, + "nullable": true, "metadata": { - "description": "Required. The DDoS protection plan ID associated with the public IP address." + "description": "Optional. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -881,12 +899,19 @@ "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." + "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": { @@ -899,12 +924,19 @@ "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 logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "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": { @@ -985,7 +1017,19 @@ }, "zones": { "type": "array", - "nullable": true, + "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." } @@ -1053,7 +1097,7 @@ "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { - "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + "description": "Optional. Array of role assignments to create." } }, "enableTelemetry": { @@ -1085,6 +1129,13 @@ } }, "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')]", @@ -1102,8 +1153,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1121,7 +1172,7 @@ }, "publicIpAddress": { "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-04-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1129,7 +1180,7 @@ "name": "[parameters('skuName')]", "tier": "[parameters('skuTier')]" }, - "zones": "[parameters('zones')]", + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", "properties": { "ddosSettings": "[parameters('ddosSettings')]", "dnsSettings": "[parameters('dnsSettings')]", @@ -1137,7 +1188,7 @@ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", - "ipTags": [] + "ipTags": null } }, "publicIpAddress_lock": { @@ -1157,20 +1208,20 @@ "publicIpAddress_roleAssignments": { "copy": { "name": "publicIpAddress_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1186,12 +1237,30 @@ "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')]", - "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", - "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, @@ -1227,14 +1296,14 @@ "metadata": { "description": "The public IP address of the public IP address resource." }, - "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]" + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]" + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" } } } @@ -1272,8 +1341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14162654774301828518" + "version": "0.29.47.4906", + "templateHash": "915174536118171652" }, "name": "VPN Gateway NAT Rules", "description": "This module deploys a Virtual Network Gateway NAT Rule.", diff --git a/avm/res/network/virtual-network-gateway/nat-rule/main.json b/avm/res/network/virtual-network-gateway/nat-rule/main.json index f2be904ec8..c23f033ec3 100644 --- a/avm/res/network/virtual-network-gateway/nat-rule/main.json +++ b/avm/res/network/virtual-network-gateway/nat-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3348839995550946472" + "version": "0.29.47.4906", + "templateHash": "915174536118171652" }, "name": "VPN Gateway NAT Rules", "description": "This module deploys a Virtual Network Gateway NAT Rule.", diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep index 7adb6a5940..346b380610 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep @@ -101,11 +101,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'db30550e-70b7-4dbe-901e-e9363b69c05f' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/virtual-network-gateway/version.json b/avm/res/network/virtual-network-gateway/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/network/virtual-network-gateway/version.json +++ b/avm/res/network/virtual-network-gateway/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/virtual-network/README.md b/avm/res/network/virtual-network/README.md index 1b38446390..1dc98d4d11 100644 --- a/avm/res/network/virtual-network/README.md +++ b/avm/res/network/virtual-network/README.md @@ -137,9 +137,21 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { } roleAssignments: [ { + name: 'f5c27a7b-9b18-4dc1-b002-db3c38e80b64' principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] subnets: [ @@ -155,7 +167,17 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { { principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] routeTableResourceId: '' @@ -273,9 +295,21 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { "roleAssignments": { "value": [ { + "name": "f5c27a7b-9b18-4dc1-b002-db3c38e80b64", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] }, @@ -293,7 +327,17 @@ module virtualNetwork 'br/public:avm/res/network/virtual-network:' = { { "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "routeTableResourceId": "", @@ -1016,6 +1060,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1066,6 +1111,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/virtual-network/main.bicep b/avm/res/network/virtual-network/main.bicep index 76dfd2383b..0ca96b3195 100644 --- a/avm/res/network/virtual-network/main.bicep +++ b/avm/res/network/virtual-network/main.bicep @@ -70,6 +70,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============ // // Dependencies // // ============ // @@ -293,12 +304,10 @@ resource virtualNetwork_diagnosticSettings 'Microsoft.Insights/diagnosticSetting ] resource virtualNetwork_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(virtualNetwork.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(virtualNetwork.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : roleAssignment.roleDefinitionIdOrName + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -343,6 +352,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/virtual-network/main.json b/avm/res/network/virtual-network/main.json index b96b14ca92..615fb8fcb0 100644 --- a/avm/res/network/virtual-network/main.json +++ b/avm/res/network/virtual-network/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7765825140175449922" + "version": "0.29.47.4906", + "templateHash": "14023358203441656898" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet).", @@ -43,6 +43,13 @@ "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": { @@ -333,6 +340,13 @@ } }, "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')]", @@ -461,20 +475,20 @@ "virtualNetwork_roleAssignments": { "copy": { "name": "virtualNetwork_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -525,8 +539,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9634407864982934565" + "version": "0.29.47.4906", + "templateHash": "17087074157977382192" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -538,6 +552,13 @@ "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": { @@ -714,6 +735,13 @@ } }, "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')]", @@ -755,20 +783,20 @@ "subnet_roleAssignments": { "copy": { "name": "subnet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -851,8 +879,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "1725716682351191048" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -996,8 +1024,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "1725716682351191048" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", diff --git a/avm/res/network/virtual-network/subnet/README.md b/avm/res/network/virtual-network/subnet/README.md index 0ebf65fa73..a8ba0cbdf9 100644 --- a/avm/res/network/virtual-network/subnet/README.md +++ b/avm/res/network/virtual-network/subnet/README.md @@ -173,6 +173,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -223,6 +224,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/virtual-network/subnet/main.bicep b/avm/res/network/virtual-network/subnet/main.bicep index 4e795632e2..1be5d4bb9a 100644 --- a/avm/res/network/virtual-network/subnet/main.bicep +++ b/avm/res/network/virtual-network/subnet/main.bicep @@ -75,6 +75,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' existing = { name: virtualNetworkName } @@ -113,12 +124,10 @@ resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { } resource subnet_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(subnet.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(subnet.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : roleAssignment.roleDefinitionIdOrName + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -150,6 +159,9 @@ output subnetAddressPrefixes array = !empty(addressPrefixes) ? subnet.properties // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/virtual-network/subnet/main.json b/avm/res/network/virtual-network/subnet/main.json index fbb05c3c69..f516267f77 100644 --- a/avm/res/network/virtual-network/subnet/main.json +++ b/avm/res/network/virtual-network/subnet/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9634407864982934565" + "version": "0.29.47.4906", + "templateHash": "17087074157977382192" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -18,6 +18,13 @@ "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": { @@ -194,6 +201,13 @@ } }, "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')]", @@ -235,20 +249,20 @@ "subnet_roleAssignments": { "copy": { "name": "subnet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep b/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep index 6111d452f3..d0ba4e7d87 100644 --- a/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep +++ b/avm/res/network/virtual-network/tests/e2e/max/main.test.bicep @@ -97,7 +97,22 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: 'f5c27a7b-9b18-4dc1-b002-db3c38e80b64' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -114,7 +129,20 @@ module testDeployment '../../../main.bicep' = [ networkSecurityGroupResourceId: nestedDependencies.outputs.networkSecurityGroupResourceId roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } diff --git a/avm/res/network/virtual-network/version.json b/avm/res/network/virtual-network/version.json index 1c884ecaa9..729ac87673 100644 --- a/avm/res/network/virtual-network/version.json +++ b/avm/res/network/virtual-network/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/virtual-network/virtual-network-peering/main.json b/avm/res/network/virtual-network/virtual-network-peering/main.json index 16988eddb0..5bbd700968 100644 --- a/avm/res/network/virtual-network/virtual-network-peering/main.json +++ b/avm/res/network/virtual-network/virtual-network-peering/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "1725716682351191048" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", diff --git a/avm/res/network/virtual-wan/README.md b/avm/res/network/virtual-wan/README.md index 0b34e1155b..45e7a88611 100644 --- a/avm/res/network/virtual-wan/README.md +++ b/avm/res/network/virtual-wan/README.md @@ -105,11 +105,13 @@ module virtualWan 'br/public:avm/res/network/virtual-wan:' = { } roleAssignments: [ { + name: '360a3e7e-49bf-4e94-839f-14c91e8e0c23' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -168,11 +170,13 @@ module virtualWan 'br/public:avm/res/network/virtual-wan:' = { "roleAssignments": { "value": [ { + "name": "360a3e7e-49bf-4e94-839f-14c91e8e0c23", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -405,6 +409,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -455,6 +460,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/virtual-wan/main.bicep b/avm/res/network/virtual-wan/main.bicep index 717c1cc6e9..915fd9e1f5 100644 --- a/avm/res/network/virtual-wan/main.bicep +++ b/avm/res/network/virtual-wan/main.bicep @@ -54,6 +54,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.network-virtualwan.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -96,15 +107,11 @@ resource virtualWan_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty scope: virtualWan } -resource networkInterface_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(virtualWan.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) +resource virtualWan_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(virtualWan.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -141,6 +148,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/virtual-wan/main.json b/avm/res/network/virtual-wan/main.json index e3f737b5a7..7903c4664f 100644 --- a/avm/res/network/virtual-wan/main.json +++ b/avm/res/network/virtual-wan/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13137323349121253704" + "version": "0.29.47.4906", + "templateHash": "13454102867116433780" }, "name": "Virtual WANs", "description": "This module deploys a Virtual WAN.", @@ -43,6 +43,13 @@ "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": { @@ -179,6 +186,13 @@ } }, "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')]", @@ -236,23 +250,23 @@ "virtualWan" ] }, - "networkInterface_roleAssignments": { + "virtualWan_roleAssignments": { "copy": { - "name": "networkInterface_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "virtualWan_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualWans/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualWans', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualWans', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "virtualWan" diff --git a/avm/res/network/virtual-wan/tests/e2e/max/main.test.bicep b/avm/res/network/virtual-wan/tests/e2e/max/main.test.bicep index 909f2c7969..f528aa85b7 100644 --- a/avm/res/network/virtual-wan/tests/e2e/max/main.test.bicep +++ b/avm/res/network/virtual-wan/tests/e2e/max/main.test.bicep @@ -61,11 +61,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '360a3e7e-49bf-4e94-839f-14c91e8e0c23' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/virtual-wan/version.json b/avm/res/network/virtual-wan/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/network/virtual-wan/version.json +++ b/avm/res/network/virtual-wan/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/vpn-site/README.md b/avm/res/network/vpn-site/README.md index faa4df700b..d7f40ec6a4 100644 --- a/avm/res/network/vpn-site/README.md +++ b/avm/res/network/vpn-site/README.md @@ -130,11 +130,13 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { } roleAssignments: [ { + name: '1dcfa9c2-5e95-42d2-bf04-bdecad93abcf' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -230,11 +232,13 @@ module vpnSite 'br/public:avm/res/network/vpn-site:' = { "roleAssignments": { "value": [ { + "name": "1dcfa9c2-5e95-42d2-bf04-bdecad93abcf", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -620,6 +624,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -670,6 +675,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/network/vpn-site/main.bicep b/avm/res/network/vpn-site/main.bicep index 23e7d5dec9..b0e6eb44ed 100644 --- a/avm/res/network/vpn-site/main.bicep +++ b/avm/res/network/vpn-site/main.bicep @@ -62,6 +62,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: take( @@ -118,14 +129,10 @@ resource vpnSite_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource vpnSite_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(vpnSite.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(vpnSite.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -162,6 +169,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/network/vpn-site/main.json b/avm/res/network/vpn-site/main.json index 5d11577c66..bb1a7614e9 100644 --- a/avm/res/network/vpn-site/main.json +++ b/avm/res/network/vpn-site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12528327237603601102" + "version": "0.29.47.4906", + "templateHash": "7657353044034170169" }, "name": "VPN Sites", "description": "This module deploys a VPN Site.", @@ -43,6 +43,13 @@ "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": { @@ -202,6 +209,13 @@ } }, "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')]", @@ -268,20 +282,20 @@ "vpnSite_roleAssignments": { "copy": { "name": "vpnSite_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/vpnSites/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/vpnSites', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/vpnSites', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "vpnSite" diff --git a/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep b/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep index bd72eae845..c3969e70e5 100644 --- a/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep +++ b/avm/res/network/vpn-site/tests/e2e/max/main.test.bicep @@ -105,11 +105,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '1dcfa9c2-5e95-42d2-bf04-bdecad93abcf' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/network/vpn-site/version.json b/avm/res/network/vpn-site/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/network/vpn-site/version.json +++ b/avm/res/network/vpn-site/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/operational-insights/workspace/README.md b/avm/res/operational-insights/workspace/README.md index 6334b728d3..825f7faf60 100644 --- a/avm/res/operational-insights/workspace/README.md +++ b/avm/res/operational-insights/workspace/README.md @@ -187,6 +187,15 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = storageAccountResourceId: '' workspaceResourceId: '' } + { + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'sendingDiagnosticSettingsToSelf' + useThisWorkspace: true + } ] gallerySolutions: [ { @@ -325,7 +334,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - useDeployedWorkspaceForDiagnosticSettings: true useResourcePermissions: true } } @@ -484,6 +492,15 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "name": "customSetting", "storageAccountResourceId": "", "workspaceResourceId": "" + }, + { + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "sendingDiagnosticSettingsToSelf", + "useThisWorkspace": true } ] }, @@ -646,9 +663,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "Role": "DeploymentValidation" } }, - "useDeployedWorkspaceForDiagnosticSettings": { - "value": true - }, "useResourcePermissions": { "value": true } @@ -859,11 +873,13 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = publicNetworkAccessForQuery: 'Disabled' roleAssignments: [ { + name: 'c3d53092-840c-4025-9c02-9bcb7895789c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1175,11 +1191,13 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = "roleAssignments": { "value": [ { + "name": "c3d53092-840c-4025-9c02-9bcb7895789c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1721,7 +1739,6 @@ module workspace 'br/public:avm/res/operational-insights/workspace:' = | [`storageInsightsConfigs`](#parameter-storageinsightsconfigs) | array | List of storage accounts to be read by the workspace. | | [`tables`](#parameter-tables) | array | LAW custom tables to be deployed. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`useDeployedWorkspaceForDiagnosticSettings`](#parameter-usedeployedworkspacefordiagnosticsettings) | bool | Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. | | [`useResourcePermissions`](#parameter-useresourcepermissions) | bool | Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. | ### Parameter: `name` @@ -1790,6 +1807,7 @@ The diagnostic settings of the service. | [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | | [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | | [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | 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. | +| [`useThisWorkspace`](#parameter-diagnosticsettingsusethisworkspace) | bool | 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`](#parameter-diagnosticsettingsworkspaceresourceid) | string | 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. | ### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` @@ -1910,6 +1928,13 @@ Resource ID of the diagnostic storage account. For security reasons, it is recom - Required: No - Type: string +### Parameter: `diagnosticSettings.useThisWorkspace` + +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. + +- Required: No +- Type: bool + ### Parameter: `diagnosticSettings.workspaceResourceId` 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. @@ -2073,6 +2098,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2123,6 +2149,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2200,14 +2233,6 @@ Tags of the resource. - Required: No - Type: object -### Parameter: `useDeployedWorkspaceForDiagnosticSettings` - -Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. - -- Required: No -- Type: bool -- Default: `False` - ### Parameter: `useResourcePermissions` Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. diff --git a/avm/res/operational-insights/workspace/data-export/main.json b/avm/res/operational-insights/workspace/data-export/main.json index e81cbbc749..93d4ebbfff 100644 --- a/avm/res/operational-insights/workspace/data-export/main.json +++ b/avm/res/operational-insights/workspace/data-export/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7136569845646186437" + "version": "0.29.47.4906", + "templateHash": "5765609820817623497" }, "name": "Log Analytics Workspace Data Exports", "description": "This module deploys a Log Analytics Workspace Data Export.", diff --git a/avm/res/operational-insights/workspace/data-source/main.json b/avm/res/operational-insights/workspace/data-source/main.json index a19289c6b8..947d1fb8c0 100644 --- a/avm/res/operational-insights/workspace/data-source/main.json +++ b/avm/res/operational-insights/workspace/data-source/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13864187993066263466" + "version": "0.29.47.4906", + "templateHash": "13460038983765020046" }, "name": "Log Analytics Workspace Datasources", "description": "This module deploys a Log Analytics Workspace Data Source.", diff --git a/avm/res/operational-insights/workspace/linked-service/main.json b/avm/res/operational-insights/workspace/linked-service/main.json index a7547a6484..7235c7ef1b 100644 --- a/avm/res/operational-insights/workspace/linked-service/main.json +++ b/avm/res/operational-insights/workspace/linked-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11327223922004680974" + "version": "0.29.47.4906", + "templateHash": "12032441371027552374" }, "name": "Log Analytics Workspace Linked Services", "description": "This module deploys a Log Analytics Workspace Linked Service.", diff --git a/avm/res/operational-insights/workspace/linked-storage-account/main.json b/avm/res/operational-insights/workspace/linked-storage-account/main.json index 57651a3c05..7c3b81ed2d 100644 --- a/avm/res/operational-insights/workspace/linked-storage-account/main.json +++ b/avm/res/operational-insights/workspace/linked-storage-account/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16393542404456450187" + "version": "0.29.47.4906", + "templateHash": "12623216644328477682" }, "name": "Log Analytics Workspace Linked Storage Accounts", "description": "This module deploys a Log Analytics Workspace Linked Storage Account.", diff --git a/avm/res/operational-insights/workspace/main.bicep b/avm/res/operational-insights/workspace/main.bicep index d7e2e80689..4c6bd5e5a1 100644 --- a/avm/res/operational-insights/workspace/main.bicep +++ b/avm/res/operational-insights/workspace/main.bicep @@ -82,9 +82,6 @@ param useResourcePermissions bool = false @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingType -@description('Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings.') -param useDeployedWorkspaceForDiagnosticSettings bool = false - @description('Optional. Indicates whether customer managed storage is mandatory for query management.') param forceCmkForQuery bool = true @@ -153,6 +150,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.operationalinsights-workspace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -201,7 +209,7 @@ resource logAnalyticsWorkspace_diagnosticSettings 'Microsoft.Insights/diagnostic name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' properties: { storageAccountId: diagnosticSetting.?storageAccountResourceId - workspaceId: useDeployedWorkspaceForDiagnosticSettings + workspaceId: (diagnosticSetting.?useThisWorkspace ?? false) ? logAnalyticsWorkspace.id : diagnosticSetting.?workspaceResourceId eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId @@ -232,8 +240,8 @@ module logAnalyticsWorkspace_storageInsightConfigs 'storage-insight-config/main. name: '${uniqueString(deployment().name, location)}-LAW-StorageInsightsConfig-${index}' params: { logAnalyticsWorkspaceName: logAnalyticsWorkspace.name - containers: contains(storageInsightsConfig, 'containers') ? storageInsightsConfig.containers : [] - tables: contains(storageInsightsConfig, 'tables') ? storageInsightsConfig.tables : [] + containers: storageInsightsConfig.?containers + tables: storageInsightsConfig.?tables storageAccountResourceId: storageInsightsConfig.storageAccountResourceId } } @@ -245,8 +253,8 @@ module logAnalyticsWorkspace_linkedServices 'linked-service/main.bicep' = [ params: { logAnalyticsWorkspaceName: logAnalyticsWorkspace.name name: linkedService.name - resourceId: contains(linkedService, 'resourceId') ? linkedService.resourceId : '' - writeAccessResourceId: contains(linkedService, 'writeAccessResourceId') ? linkedService.writeAccessResourceId : '' + resourceId: linkedService.?resourceId + writeAccessResourceId: linkedService.?writeAccessResourceId } } ] @@ -288,9 +296,9 @@ module logAnalyticsWorkspace_dataExports 'data-export/main.bicep' = [ params: { workspaceName: logAnalyticsWorkspace.name name: dataExport.name - destination: contains(dataExport, 'destination') ? dataExport.destination : {} - enable: contains(dataExport, 'enable') ? dataExport.enable : false - tableNames: contains(dataExport, 'tableNames') ? dataExport.tableNames : [] + destination: dataExport.?destination + enable: dataExport.?enable + tableNames: dataExport.?tableNames } } ] @@ -302,17 +310,17 @@ module logAnalyticsWorkspace_dataSources 'data-source/main.bicep' = [ logAnalyticsWorkspaceName: logAnalyticsWorkspace.name name: dataSource.name kind: dataSource.kind - linkedResourceId: contains(dataSource, 'linkedResourceId') ? dataSource.linkedResourceId : '' - eventLogName: contains(dataSource, 'eventLogName') ? dataSource.eventLogName : '' - eventTypes: contains(dataSource, 'eventTypes') ? dataSource.eventTypes : [] - objectName: contains(dataSource, 'objectName') ? dataSource.objectName : '' - instanceName: contains(dataSource, 'instanceName') ? dataSource.instanceName : '' - intervalSeconds: contains(dataSource, 'intervalSeconds') ? dataSource.intervalSeconds : 60 - counterName: contains(dataSource, 'counterName') ? dataSource.counterName : '' - state: contains(dataSource, 'state') ? dataSource.state : '' - syslogName: contains(dataSource, 'syslogName') ? dataSource.syslogName : '' - syslogSeverities: contains(dataSource, 'syslogSeverities') ? dataSource.syslogSeverities : [] - performanceCounters: contains(dataSource, 'performanceCounters') ? dataSource.performanceCounters : [] + linkedResourceId: dataSource.?linkedResourceId + eventLogName: dataSource.?eventLogName + eventTypes: dataSource.?eventTypes + objectName: dataSource.?objectName + instanceName: dataSource.?instanceName + intervalSeconds: dataSource.?intervalSeconds + counterName: dataSource.?counterName + state: dataSource.?state + syslogName: dataSource.?syslogName + syslogSeverities: dataSource.?syslogSeverities + performanceCounters: dataSource.?performanceCounters } } ] @@ -341,8 +349,8 @@ module logAnalyticsWorkspace_solutions 'br/public:avm/res/operations-management/ name: gallerySolution.name location: location logAnalyticsWorkspaceName: logAnalyticsWorkspace.name - product: contains(gallerySolution, 'product') ? gallerySolution.product : 'OMSGallery' - publisher: contains(gallerySolution, 'publisher') ? gallerySolution.publisher : 'Microsoft' + product: gallerySolution.?product + publisher: gallerySolution.?publisher enableTelemetry: gallerySolution.?enableTelemetry ?? enableTelemetry } } @@ -360,14 +368,14 @@ resource logAnalyticsWorkspace_lock 'Microsoft.Authorization/locks@2020-05-01' = } resource logAnalyticsWorkspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(logAnalyticsWorkspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + logAnalyticsWorkspace.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -418,6 +426,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -468,6 +479,9 @@ type diagnosticSettingType = { @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.') logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics' | null)? + @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.') + useThisWorkspace: bool? + @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.') workspaceResourceId: string? diff --git a/avm/res/operational-insights/workspace/main.json b/avm/res/operational-insights/workspace/main.json index f0dcd643b6..ccbd83e3c1 100644 --- a/avm/res/operational-insights/workspace/main.json +++ b/avm/res/operational-insights/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4858897614510907922" + "version": "0.29.47.4906", + "templateHash": "11994891550110156380" }, "name": "Log Analytics Workspaces", "description": "This module deploys a Log Analytics Workspace.", @@ -66,6 +66,13 @@ "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": { @@ -208,6 +215,13 @@ "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, @@ -402,13 +416,6 @@ "description": "Optional. The diagnostic settings of the service." } }, - "useDeployedWorkspaceForDiagnosticSettings": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings." - } - }, "forceCmkForQuery": { "type": "bool", "defaultValue": true, @@ -444,6 +451,13 @@ } }, "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()), 'SystemAssigned', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -537,7 +551,7 @@ } ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[if(parameters('useDeployedWorkspaceForDiagnosticSettings'), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId'))]", + "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')]", @@ -564,20 +578,20 @@ "logAnalyticsWorkspace_roleAssignments": { "copy": { "name": "logAnalyticsWorkspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -600,8 +614,12 @@ "logAnalyticsWorkspaceName": { "value": "[parameters('name')]" }, - "containers": "[if(contains(parameters('storageInsightsConfigs')[copyIndex()], 'containers'), createObject('value', parameters('storageInsightsConfigs')[copyIndex()].containers), createObject('value', createArray()))]", - "tables": "[if(contains(parameters('storageInsightsConfigs')[copyIndex()], 'tables'), createObject('value', parameters('storageInsightsConfigs')[copyIndex()].tables), createObject('value', createArray()))]", + "containers": { + "value": "[tryGet(parameters('storageInsightsConfigs')[copyIndex()], 'containers')]" + }, + "tables": { + "value": "[tryGet(parameters('storageInsightsConfigs')[copyIndex()], 'tables')]" + }, "storageAccountResourceId": { "value": "[parameters('storageInsightsConfigs')[copyIndex()].storageAccountResourceId]" } @@ -613,8 +631,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16775981135504794519" + "version": "0.29.47.4906", + "templateHash": "1745671120474305926" }, "name": "Log Analytics Workspace Storage Insight Configs", "description": "This module deploys a Log Analytics Workspace Storage Insight Config.", @@ -743,8 +761,12 @@ "name": { "value": "[parameters('linkedServices')[copyIndex()].name]" }, - "resourceId": "[if(contains(parameters('linkedServices')[copyIndex()], 'resourceId'), createObject('value', parameters('linkedServices')[copyIndex()].resourceId), createObject('value', ''))]", - "writeAccessResourceId": "[if(contains(parameters('linkedServices')[copyIndex()], 'writeAccessResourceId'), createObject('value', parameters('linkedServices')[copyIndex()].writeAccessResourceId), createObject('value', ''))]" + "resourceId": { + "value": "[tryGet(parameters('linkedServices')[copyIndex()], 'resourceId')]" + }, + "writeAccessResourceId": { + "value": "[tryGet(parameters('linkedServices')[copyIndex()], 'writeAccessResourceId')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -753,8 +775,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11327223922004680974" + "version": "0.29.47.4906", + "templateHash": "12032441371027552374" }, "name": "Log Analytics Workspace Linked Services", "description": "This module deploys a Log Analytics Workspace Linked Service.", @@ -875,8 +897,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16393542404456450187" + "version": "0.29.47.4906", + "templateHash": "12623216644328477682" }, "name": "Log Analytics Workspace Linked Storage Accounts", "description": "This module deploys a Log Analytics Workspace Linked Storage Account.", @@ -998,8 +1020,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18166536793397048488" + "version": "0.29.47.4906", + "templateHash": "7683333179440464721" }, "name": "Log Analytics Workspace Saved Searches", "description": "This module deploys a Log Analytics Workspace Saved Search.", @@ -1148,9 +1170,15 @@ "name": { "value": "[parameters('dataExports')[copyIndex()].name]" }, - "destination": "[if(contains(parameters('dataExports')[copyIndex()], 'destination'), createObject('value', parameters('dataExports')[copyIndex()].destination), createObject('value', createObject()))]", - "enable": "[if(contains(parameters('dataExports')[copyIndex()], 'enable'), createObject('value', parameters('dataExports')[copyIndex()].enable), createObject('value', false()))]", - "tableNames": "[if(contains(parameters('dataExports')[copyIndex()], 'tableNames'), createObject('value', parameters('dataExports')[copyIndex()].tableNames), createObject('value', createArray()))]" + "destination": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'destination')]" + }, + "enable": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'enable')]" + }, + "tableNames": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'tableNames')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1158,8 +1186,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7136569845646186437" + "version": "0.29.47.4906", + "templateHash": "5765609820817623497" }, "name": "Log Analytics Workspace Data Exports", "description": "This module deploys a Log Analytics Workspace Data Export.", @@ -1266,17 +1294,39 @@ "kind": { "value": "[parameters('dataSources')[copyIndex()].kind]" }, - "linkedResourceId": "[if(contains(parameters('dataSources')[copyIndex()], 'linkedResourceId'), createObject('value', parameters('dataSources')[copyIndex()].linkedResourceId), createObject('value', ''))]", - "eventLogName": "[if(contains(parameters('dataSources')[copyIndex()], 'eventLogName'), createObject('value', parameters('dataSources')[copyIndex()].eventLogName), createObject('value', ''))]", - "eventTypes": "[if(contains(parameters('dataSources')[copyIndex()], 'eventTypes'), createObject('value', parameters('dataSources')[copyIndex()].eventTypes), createObject('value', createArray()))]", - "objectName": "[if(contains(parameters('dataSources')[copyIndex()], 'objectName'), createObject('value', parameters('dataSources')[copyIndex()].objectName), createObject('value', ''))]", - "instanceName": "[if(contains(parameters('dataSources')[copyIndex()], 'instanceName'), createObject('value', parameters('dataSources')[copyIndex()].instanceName), createObject('value', ''))]", - "intervalSeconds": "[if(contains(parameters('dataSources')[copyIndex()], 'intervalSeconds'), createObject('value', parameters('dataSources')[copyIndex()].intervalSeconds), createObject('value', 60))]", - "counterName": "[if(contains(parameters('dataSources')[copyIndex()], 'counterName'), createObject('value', parameters('dataSources')[copyIndex()].counterName), createObject('value', ''))]", - "state": "[if(contains(parameters('dataSources')[copyIndex()], 'state'), createObject('value', parameters('dataSources')[copyIndex()].state), createObject('value', ''))]", - "syslogName": "[if(contains(parameters('dataSources')[copyIndex()], 'syslogName'), createObject('value', parameters('dataSources')[copyIndex()].syslogName), createObject('value', ''))]", - "syslogSeverities": "[if(contains(parameters('dataSources')[copyIndex()], 'syslogSeverities'), createObject('value', parameters('dataSources')[copyIndex()].syslogSeverities), createObject('value', createArray()))]", - "performanceCounters": "[if(contains(parameters('dataSources')[copyIndex()], 'performanceCounters'), createObject('value', parameters('dataSources')[copyIndex()].performanceCounters), createObject('value', createArray()))]" + "linkedResourceId": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'linkedResourceId')]" + }, + "eventLogName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'eventLogName')]" + }, + "eventTypes": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'eventTypes')]" + }, + "objectName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'objectName')]" + }, + "instanceName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'instanceName')]" + }, + "intervalSeconds": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'intervalSeconds')]" + }, + "counterName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'counterName')]" + }, + "state": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'state')]" + }, + "syslogName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'syslogName')]" + }, + "syslogSeverities": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'syslogSeverities')]" + }, + "performanceCounters": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'performanceCounters')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1285,8 +1335,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13864187993066263466" + "version": "0.29.47.4906", + "templateHash": "13460038983765020046" }, "name": "Log Analytics Workspace Datasources", "description": "This module deploys a Log Analytics Workspace Data Source.", @@ -1516,8 +1566,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9332283068393802804" + "version": "0.29.47.4906", + "templateHash": "6905244456918791391" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table.", @@ -1529,6 +1579,13 @@ "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": { @@ -1662,6 +1719,13 @@ } }, "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')]", @@ -1700,20 +1764,20 @@ "table_roleAssignments": { "copy": { "name": "table_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1773,8 +1837,12 @@ "logAnalyticsWorkspaceName": { "value": "[parameters('name')]" }, - "product": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'product'), createObject('value', parameters('gallerySolutions')[copyIndex()].product), createObject('value', 'OMSGallery'))]", - "publisher": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'publisher'), createObject('value', parameters('gallerySolutions')[copyIndex()].publisher), createObject('value', 'Microsoft'))]", + "product": { + "value": "[tryGet(parameters('gallerySolutions')[copyIndex()], 'product')]" + }, + "publisher": { + "value": "[tryGet(parameters('gallerySolutions')[copyIndex()], 'publisher')]" + }, "enableTelemetry": { "value": "[coalesce(tryGet(parameters('gallerySolutions')[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" } diff --git a/avm/res/operational-insights/workspace/saved-search/main.json b/avm/res/operational-insights/workspace/saved-search/main.json index c6052c5f18..88711e3f27 100644 --- a/avm/res/operational-insights/workspace/saved-search/main.json +++ b/avm/res/operational-insights/workspace/saved-search/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18166536793397048488" + "version": "0.29.47.4906", + "templateHash": "7683333179440464721" }, "name": "Log Analytics Workspace Saved Searches", "description": "This module deploys a Log Analytics Workspace Saved Search.", diff --git a/avm/res/operational-insights/workspace/storage-insight-config/main.json b/avm/res/operational-insights/workspace/storage-insight-config/main.json index 76a9026854..2c3b360f01 100644 --- a/avm/res/operational-insights/workspace/storage-insight-config/main.json +++ b/avm/res/operational-insights/workspace/storage-insight-config/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16775981135504794519" + "version": "0.29.47.4906", + "templateHash": "1745671120474305926" }, "name": "Log Analytics Workspace Storage Insight Configs", "description": "This module deploys a Log Analytics Workspace Storage Insight Config.", diff --git a/avm/res/operational-insights/workspace/table/README.md b/avm/res/operational-insights/workspace/table/README.md index 60fd147c20..b9b15ba15f 100644 --- a/avm/res/operational-insights/workspace/table/README.md +++ b/avm/res/operational-insights/workspace/table/README.md @@ -110,6 +110,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -160,6 +161,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/operational-insights/workspace/table/main.bicep b/avm/res/operational-insights/workspace/table/main.bicep index 3bbbc6f38b..cee095b8d2 100644 --- a/avm/res/operational-insights/workspace/table/main.bicep +++ b/avm/res/operational-insights/workspace/table/main.bicep @@ -71,6 +71,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // =============== // // Deployments // // =============== // @@ -93,14 +104,10 @@ resource table 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' = { } resource table_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(table.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(table.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -130,6 +137,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/operational-insights/workspace/table/main.json b/avm/res/operational-insights/workspace/table/main.json index b205b49169..18cdd3e09b 100644 --- a/avm/res/operational-insights/workspace/table/main.json +++ b/avm/res/operational-insights/workspace/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9332283068393802804" + "version": "0.29.47.4906", + "templateHash": "6905244456918791391" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table.", @@ -18,6 +18,13 @@ "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": { @@ -151,6 +158,13 @@ } }, "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')]", @@ -189,20 +203,20 @@ "table_roleAssignments": { "copy": { "name": "table_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep index e0025415db..94d627e6a7 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/adv/main.test.bicep @@ -174,8 +174,16 @@ module testDeployment '../../../main.bicep' = [ storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } + { + name: 'sendingDiagnosticSettingsToSelf' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + useThisWorkspace: true + } ] - useDeployedWorkspaceForDiagnosticSettings: true gallerySolutions: [ { name: 'AzureAutomation' diff --git a/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep b/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep index 6de957e29f..4b277bc3f5 100644 --- a/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/operational-insights/workspace/tests/e2e/max/main.test.bicep @@ -330,11 +330,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'c3d53092-840c-4025-9c02-9bcb7895789c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/operational-insights/workspace/version.json b/avm/res/operational-insights/workspace/version.json index 3f863a2bec..e42c3d9e5f 100644 --- a/avm/res/operational-insights/workspace/version.json +++ b/avm/res/operational-insights/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/portal/dashboard/README.md b/avm/res/portal/dashboard/README.md index a589618878..2703a872c1 100644 --- a/avm/res/portal/dashboard/README.md +++ b/avm/res/portal/dashboard/README.md @@ -206,11 +206,13 @@ module dashboard 'br/public:avm/res/portal/dashboard:' = { } roleAssignments: [ { + name: '15e2e690-5c9f-4cbf-9716-94ee73efab8b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -367,11 +369,13 @@ module dashboard 'br/public:avm/res/portal/dashboard:' = { "roleAssignments": { "value": [ { + "name": "15e2e690-5c9f-4cbf-9716-94ee73efab8b", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -810,6 +814,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -860,6 +865,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/portal/dashboard/main.bicep b/avm/res/portal/dashboard/main.bicep index 8775b79e53..b732ee714c 100644 --- a/avm/res/portal/dashboard/main.bicep +++ b/avm/res/portal/dashboard/main.bicep @@ -41,6 +41,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -75,14 +86,10 @@ resource dashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { } resource dashboard_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(dashboard.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(dashboard.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -126,6 +133,9 @@ output location string = dashboard.location // ================ // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/portal/dashboard/main.json b/avm/res/portal/dashboard/main.json index 43e70d7b3a..32fcb39e93 100644 --- a/avm/res/portal/dashboard/main.json +++ b/avm/res/portal/dashboard/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3374299818365126062" + "version": "0.29.47.4906", + "templateHash": "4329413484700617362" }, "name": "Portal Dashboards", "description": "This module deploys a Portal Dashboard.", @@ -18,6 +18,13 @@ "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": { @@ -164,6 +171,13 @@ } }, "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')]", @@ -207,20 +221,20 @@ "dashboard_roleAssignments": { "copy": { "name": "dashboard_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Portal/dashboards/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Portal/dashboards', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Portal/dashboards', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "dashboard" diff --git a/avm/res/portal/dashboard/tests/e2e/max/main.test.bicep b/avm/res/portal/dashboard/tests/e2e/max/main.test.bicep index 1cba1e978f..44cc1b3604 100644 --- a/avm/res/portal/dashboard/tests/e2e/max/main.test.bicep +++ b/avm/res/portal/dashboard/tests/e2e/max/main.test.bicep @@ -54,11 +54,13 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation roleAssignments: [ { + name: '15e2e690-5c9f-4cbf-9716-94ee73efab8b' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/portal/dashboard/version.json b/avm/res/portal/dashboard/version.json index 8def869ede..729ac87673 100644 --- a/avm/res/portal/dashboard/version.json +++ b/avm/res/portal/dashboard/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/purview/account/README.md b/avm/res/purview/account/README.md index 5f52f7ce62..ce0a389913 100644 --- a/avm/res/purview/account/README.md +++ b/avm/res/purview/account/README.md @@ -23,8 +23,8 @@ This module deploys a Purview Account. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Purview/accounts` | [2021-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Purview/2021-07-01/accounts) | ## Usage examples @@ -105,9 +105,13 @@ module account 'br/public:avm/res/purview/account:' = { // Non-required parameters accountPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'account' subnetResourceId: '' tags: { @@ -133,9 +137,13 @@ module account 'br/public:avm/res/purview/account:' = { ] eventHubPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' tags: { @@ -158,9 +166,13 @@ module account 'br/public:avm/res/purview/account:' = { managedResourceGroupName: 'pvamax001-managed-rg' portalPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'portal' subnetResourceId: '' tags: { @@ -170,20 +182,26 @@ module account 'br/public:avm/res/purview/account:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: '8372742c-408e-4a8a-a748-aca787a0e33e' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -196,9 +214,13 @@ module account 'br/public:avm/res/purview/account:' = { ] storageBlobPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'blob' subnetResourceId: '' tags: { @@ -210,9 +232,13 @@ module account 'br/public:avm/res/purview/account:' = { ] storageQueuePrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'queue' subnetResourceId: '' tags: { @@ -222,9 +248,13 @@ module account 'br/public:avm/res/purview/account:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -257,9 +287,13 @@ module account 'br/public:avm/res/purview/account:' = { "accountPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "account", "subnetResourceId": "", "tags": { @@ -289,9 +323,13 @@ module account 'br/public:avm/res/purview/account:' = { "eventHubPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "", "tags": { @@ -324,9 +362,13 @@ module account 'br/public:avm/res/purview/account:' = { "portalPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "portal", "subnetResourceId": "", "tags": { @@ -336,9 +378,13 @@ module account 'br/public:avm/res/purview/account:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -349,11 +395,13 @@ module account 'br/public:avm/res/purview/account:' = { "roleAssignments": { "value": [ { + "name": "8372742c-408e-4a8a-a748-aca787a0e33e", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -368,9 +416,13 @@ module account 'br/public:avm/res/purview/account:' = { "storageBlobPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "blob", "subnetResourceId": "", "tags": { @@ -384,9 +436,13 @@ module account 'br/public:avm/res/purview/account:' = { "storageQueuePrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "queue", "subnetResourceId": "", "tags": { @@ -396,9 +452,13 @@ module account 'br/public:avm/res/purview/account:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -435,9 +495,13 @@ module account 'br/public:avm/res/purview/account:' = { // Non-required parameters accountPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'account' subnetResourceId: '' } @@ -452,9 +516,13 @@ module account 'br/public:avm/res/purview/account:' = { ] eventHubPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' } @@ -463,9 +531,13 @@ module account 'br/public:avm/res/purview/account:' = { managedResourceGroupName: 'pvawaf001-managed-rg' portalPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'portal' subnetResourceId: '' } @@ -473,18 +545,26 @@ module account 'br/public:avm/res/purview/account:' = { publicNetworkAccess: 'Disabled' storageBlobPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'blob' subnetResourceId: '' } ] storageQueuePrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'queue' subnetResourceId: '' } @@ -518,9 +598,13 @@ module account 'br/public:avm/res/purview/account:' = { "accountPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "account", "subnetResourceId": "" } @@ -539,9 +623,13 @@ module account 'br/public:avm/res/purview/account:' = { "eventHubPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "" } @@ -556,9 +644,13 @@ module account 'br/public:avm/res/purview/account:' = { "portalPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "portal", "subnetResourceId": "" } @@ -570,9 +662,13 @@ module account 'br/public:avm/res/purview/account:' = { "storageBlobPrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "blob", "subnetResourceId": "" } @@ -581,9 +677,13 @@ module account 'br/public:avm/res/purview/account:' = { "storageQueuePrivateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "queue", "subnetResourceId": "" } @@ -665,8 +765,7 @@ Configuration details for Purview Account private endpoints. For security reason | [`lock`](#parameter-accountprivateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-accountprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-accountprivateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-accountprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-accountprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-accountprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-accountprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-accountprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-accountprivateendpointsroleassignments) | array | Array of role assignments to create. | @@ -850,19 +949,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `accountPrivateEndpoints.privateDnsZoneGroupName` +### Parameter: `accountPrivateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-accountprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-accountprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `accountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-accountprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-accountprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `accountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `accountPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `accountPrivateEndpoints.privateDnsZoneResourceIds` +### Parameter: `accountPrivateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `accountPrivateEndpoints.privateLinkServiceConnectionName` @@ -900,6 +1044,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-accountprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-accountprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-accountprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-accountprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-accountprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `accountPrivateEndpoints.roleAssignments.principalId` @@ -950,6 +1095,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `accountPrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `accountPrivateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1162,8 +1314,7 @@ Configuration details for Purview Managed Event Hub namespace private endpoints. | [`lock`](#parameter-eventhubprivateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-eventhubprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-eventhubprivateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-eventhubprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-eventhubprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-eventhubprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-eventhubprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-eventhubprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-eventhubprivateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1347,19 +1498,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroupName` +### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-eventhubprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-eventhubprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-eventhubprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-eventhubprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `eventHubPrivateEndpoints.privateDnsZoneResourceIds` +### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `eventHubPrivateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `eventHubPrivateEndpoints.privateLinkServiceConnectionName` @@ -1397,6 +1593,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-eventhubprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-eventhubprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-eventhubprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-eventhubprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-eventhubprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `eventHubPrivateEndpoints.roleAssignments.principalId` @@ -1447,6 +1644,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `eventHubPrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `eventHubPrivateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1577,8 +1781,7 @@ Configuration details for Purview Portal private endpoints. For security reasons | [`lock`](#parameter-portalprivateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-portalprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-portalprivateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-portalprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-portalprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-portalprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-portalprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-portalprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-portalprivateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1762,19 +1965,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `portalPrivateEndpoints.privateDnsZoneGroupName` +### Parameter: `portalPrivateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-portalprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-portalprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `portalPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-portalprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-portalprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `portalPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `portalPrivateEndpoints.privateDnsZoneResourceIds` +### Parameter: `portalPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `portalPrivateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `portalPrivateEndpoints.privateLinkServiceConnectionName` @@ -1812,6 +2060,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-portalprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-portalprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-portalprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-portalprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-portalprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `portalPrivateEndpoints.roleAssignments.principalId` @@ -1862,6 +2111,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `portalPrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `portalPrivateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1931,6 +2187,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1981,6 +2238,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2025,8 +2289,7 @@ Configuration details for Purview Managed Storage Account blob private endpoints | [`lock`](#parameter-storageblobprivateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-storageblobprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-storageblobprivateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-storageblobprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-storageblobprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-storageblobprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-storageblobprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-storageblobprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-storageblobprivateendpointsroleassignments) | array | Array of role assignments to create. | @@ -2210,19 +2473,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroupName` +### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-storageblobprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageblobprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-storageblobprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageblobprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneResourceIds` +### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `storageBlobPrivateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `storageBlobPrivateEndpoints.privateLinkServiceConnectionName` @@ -2260,6 +2568,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-storageblobprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-storageblobprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-storageblobprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-storageblobprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-storageblobprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `storageBlobPrivateEndpoints.roleAssignments.principalId` @@ -2310,6 +2619,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `storageBlobPrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `storageBlobPrivateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2368,8 +2684,7 @@ Configuration details for Purview Managed Storage Account queue private endpoint | [`lock`](#parameter-storagequeueprivateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-storagequeueprivateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-storagequeueprivateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-storagequeueprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-storagequeueprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-storagequeueprivateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-storagequeueprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-storagequeueprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-storagequeueprivateendpointsroleassignments) | array | Array of role assignments to create. | @@ -2553,19 +2868,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroupName` +### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-storagequeueprivateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storagequeueprivateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-storagequeueprivateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storagequeueprivateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneResourceIds` +### Parameter: `storageQueuePrivateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `storageQueuePrivateEndpoints.privateLinkServiceConnectionName` @@ -2603,6 +2963,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-storagequeueprivateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-storagequeueprivateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-storagequeueprivateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-storagequeueprivateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-storagequeueprivateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `storageQueuePrivateEndpoints.roleAssignments.principalId` @@ -2653,6 +3014,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `storageQueuePrivateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `storageQueuePrivateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2696,14 +3064,19 @@ Tags of the resource. | Output | Type | Description | | :-- | :-- | :-- | +| `accountPrivateEndpoints` | array | The private endpoints of the Purview Account. | +| `eventHubPrivateEndpoints` | array | The private endpoints of the managed Event Hub Namespace. | | `location` | string | The location the resource was deployed into. | | `managedEventHubId` | string | The resource ID of the managed Event Hub Namespace. | | `managedResourceGroupId` | string | The resource ID of the managed resource group. | | `managedResourceGroupName` | string | The name of the managed resource group. | | `managedStorageAccountId` | string | The resource ID of the managed storage account. | | `name` | string | The name of the Purview Account. | +| `portalPrivateEndpoints` | array | The private endpoints of the Purview Account Portal. | | `resourceGroupName` | string | The resource group the Purview Account was deployed into. | | `resourceId` | string | The resource ID of the Purview Account. | +| `storageBlobPrivateEndpoints` | array | The private endpoints of the managed storage account blob service. | +| `storageQueuePrivateEndpoints` | array | The private endpoints of the managed storage account queue service. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | ## Cross-referenced modules @@ -2712,7 +3085,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/purview/account/main.bicep b/avm/res/purview/account/main.bicep index 55e6933f3f..392942cbf6 100644 --- a/avm/res/purview/account/main.bicep +++ b/avm/res/purview/account/main.bicep @@ -83,6 +83,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.purview-account.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -154,9 +165,9 @@ resource account_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021- } ] -module account_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module account_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (accountPrivateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Account-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-account-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'account'}-${index}' @@ -195,8 +206,7 @@ module account_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -207,9 +217,9 @@ module account_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4. } ] -module portal_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module portal_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (portalPrivateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Portal-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-portal-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'portal'}-${index}' @@ -248,8 +258,7 @@ module portal_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -260,9 +269,9 @@ module portal_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1 } ] -module blob_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module blob_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (storageBlobPrivateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Storage-Blob-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-blob-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'blob'}-${index}' @@ -301,8 +310,7 @@ module blob_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -313,9 +321,9 @@ module blob_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' } ] -module queue_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module queue_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (storageQueuePrivateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Storage-Queue-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-queue-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'queue'}-${index}' @@ -354,8 +362,7 @@ module queue_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -366,9 +373,9 @@ module queue_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' } ] -module eventHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module eventHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (eventHubPrivateEndpoints ?? []): { - name: '${uniqueString(deployment().name, location)}-Eventhub-Namespace-PrivateEndpoint-${index}' + name: '${uniqueString(deployment().name, location)}-eventHub-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') params: { name: privateEndpoint.?name ?? 'pep-${last(split(account.id, '/'))}-${privateEndpoint.?service ?? 'namespace'}-${index}' @@ -407,8 +414,7 @@ module eventHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -420,14 +426,10 @@ module eventHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4 ] resource account_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(account.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(account.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -466,6 +468,61 @@ output managedEventHubId string = account.properties.managedResources.eventHubNa @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = account.?identity.?principalId ?? '' +@description('The private endpoints of the Purview Account.') +output accountPrivateEndpoints array = [ + for (pe, i) in (!empty(accountPrivateEndpoints) ? array(accountPrivateEndpoints) : []): { + name: account_privateEndpoints[i].outputs.name + resourceId: account_privateEndpoints[i].outputs.resourceId + groupId: account_privateEndpoints[i].outputs.groupId + customDnsConfig: account_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: account_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The private endpoints of the Purview Account Portal.') +output portalPrivateEndpoints array = [ + for (pe, i) in (!empty(portalPrivateEndpoints) ? array(portalPrivateEndpoints) : []): { + name: portal_privateEndpoints[i].outputs.name + resourceId: portal_privateEndpoints[i].outputs.resourceId + groupId: portal_privateEndpoints[i].outputs.groupId + customDnsConfig: portal_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: portal_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The private endpoints of the managed storage account blob service.') +output storageBlobPrivateEndpoints array = [ + for (pe, i) in (!empty(storageBlobPrivateEndpoints) ? array(storageBlobPrivateEndpoints) : []): { + name: blob_privateEndpoints[i].outputs.name + resourceId: blob_privateEndpoints[i].outputs.resourceId + groupId: blob_privateEndpoints[i].outputs.groupId + customDnsConfig: blob_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: blob_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The private endpoints of the managed storage account queue service.') +output storageQueuePrivateEndpoints array = [ + for (pe, i) in (!empty(storageQueuePrivateEndpoints) ? array(storageQueuePrivateEndpoints) : []): { + name: queue_privateEndpoints[i].outputs.name + resourceId: queue_privateEndpoints[i].outputs.resourceId + groupId: queue_privateEndpoints[i].outputs.groupId + customDnsConfig: queue_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: queue_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The private endpoints of the managed Event Hub Namespace.') +output eventHubPrivateEndpoints array = [ + for (pe, i) in (!empty(eventHubPrivateEndpoints) ? array(eventHubPrivateEndpoints) : []): { + name: eventHub_privateEndpoints[i].outputs.name + resourceId: eventHub_privateEndpoints[i].outputs.resourceId + groupId: eventHub_privateEndpoints[i].outputs.groupId + customDnsConfig: eventHub_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: eventHub_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -484,6 +541,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -560,18 +620,26 @@ type privateEndpointType = { @description('Optional. The name of the private link connection to create.') privateLinkServiceConnectionName: string? - // Variant 1: A default service can be assumed (i.e., for services that only have one private endpoint type) @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/purview/account/main.json b/avm/res/purview/account/main.json index df4a35a045..f4cf2712e1 100644 --- a/avm/res/purview/account/main.json +++ b/avm/res/purview/account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5604438782746822847" + "version": "0.29.47.4906", + "templateHash": "15039051778035234501" }, "name": "Purview Accounts", "description": "This module deploys a Purview Account.", @@ -58,6 +58,13 @@ "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": { @@ -278,21 +285,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -542,6 +572,13 @@ } }, "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": { "type": "[if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned')]", @@ -647,20 +684,20 @@ "account_roleAssignments": { "copy": { "name": "account_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Purview/accounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Purview/accounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Purview/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "account" @@ -673,7 +710,7 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Account-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-account-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "resourceGroup": "[coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { @@ -698,11 +735,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('accountPrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -730,19 +764,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -846,13 +908,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -886,8 +948,11 @@ "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." + "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": { @@ -927,8 +992,11 @@ "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." + "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": { @@ -976,6 +1044,29 @@ } }, "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": { @@ -1011,18 +1102,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1078,6 +1162,13 @@ } }, "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')]", @@ -1095,8 +1186,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1114,7 +1205,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1155,27 +1246,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1186,28 +1277,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1215,12 +1330,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1234,27 +1352,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1312,14 +1439,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1335,7 +1476,7 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Portal-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-portal-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "resourceGroup": "[coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { @@ -1360,11 +1501,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('portalPrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1392,19 +1530,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1508,13 +1674,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1548,8 +1714,11 @@ "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." + "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": { @@ -1589,8 +1758,11 @@ "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." + "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": { @@ -1638,6 +1810,29 @@ } }, "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": { @@ -1673,18 +1868,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1740,6 +1928,13 @@ } }, "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')]", @@ -1757,8 +1952,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1776,7 +1971,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1817,27 +2012,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1848,28 +2043,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1877,12 +2096,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1896,27 +2118,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1974,14 +2205,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1997,7 +2242,7 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-Blob-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-blob-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "resourceGroup": "[coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { @@ -2022,11 +2267,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('storageBlobPrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2054,19 +2296,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2170,13 +2440,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2210,8 +2480,11 @@ "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." + "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": { @@ -2251,8 +2524,11 @@ "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." + "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": { @@ -2300,6 +2576,29 @@ } }, "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": { @@ -2335,18 +2634,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2402,6 +2694,13 @@ } }, "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')]", @@ -2419,8 +2718,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2438,7 +2737,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2479,27 +2778,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -2510,28 +2809,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -2539,12 +2862,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -2558,27 +2884,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2636,14 +2971,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2659,7 +3008,7 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Storage-Queue-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-queue-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "resourceGroup": "[coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { @@ -2684,11 +3033,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('storageQueuePrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2716,19 +3062,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2832,13 +3206,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2872,8 +3246,11 @@ "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." + "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": { @@ -2913,8 +3290,11 @@ "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." + "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": { @@ -2962,6 +3342,29 @@ } }, "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": { @@ -2997,18 +3400,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -3064,6 +3460,13 @@ } }, "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')]", @@ -3081,8 +3484,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3100,7 +3503,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3141,27 +3544,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -3172,28 +3575,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -3201,12 +3628,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -3220,27 +3650,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -3298,14 +3737,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -3321,7 +3774,7 @@ }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('{0}-Eventhub-Namespace-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "name": "[format('{0}-eventHub-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "resourceGroup": "[coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", "properties": { "expressionEvaluationOptions": { @@ -3346,11 +3799,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('eventHubPrivateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -3378,19 +3828,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -3494,13 +3972,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -3534,8 +4012,11 @@ "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." + "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": { @@ -3575,8 +4056,11 @@ "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." + "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": { @@ -3624,6 +4108,29 @@ } }, "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": { @@ -3659,18 +4166,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -3726,6 +4226,13 @@ } }, "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')]", @@ -3743,8 +4250,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3762,7 +4269,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3803,27 +4310,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -3834,28 +4341,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -3863,12 +4394,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -3882,27 +4416,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -3960,14 +4503,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -4040,6 +4597,86 @@ "description": "The principal ID of the system assigned identity." }, "value": "[coalesce(tryGet(tryGet(reference('account', '2021-07-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "accountPrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Purview Account." + }, + "copy": { + "count": "[length(if(not(empty(parameters('accountPrivateEndpoints'))), array(parameters('accountPrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('account_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('account_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('account_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('account_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('account_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "portalPrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Purview Account Portal." + }, + "copy": { + "count": "[length(if(not(empty(parameters('portalPrivateEndpoints'))), array(parameters('portalPrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('portal_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('portal_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('portal_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('portal_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('portal_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "storageBlobPrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the managed storage account blob service." + }, + "copy": { + "count": "[length(if(not(empty(parameters('storageBlobPrivateEndpoints'))), array(parameters('storageBlobPrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('blob_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('blob_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('blob_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('blob_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('blob_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "storageQueuePrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the managed storage account queue service." + }, + "copy": { + "count": "[length(if(not(empty(parameters('storageQueuePrivateEndpoints'))), array(parameters('storageQueuePrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('queue_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('queue_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('queue_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('queue_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('queue_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "eventHubPrivateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the managed Event Hub Namespace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('eventHubPrivateEndpoints'))), array(parameters('eventHubPrivateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('eventHub_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('eventHub_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('eventHub_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('eventHub_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('eventHub_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/purview/account/tests/e2e/defaults/main.test.bicep b/avm/res/purview/account/tests/e2e/defaults/main.test.bicep index d1f45f3886..a8183e738b 100644 --- a/avm/res/purview/account/tests/e2e/defaults/main.test.bicep +++ b/avm/res/purview/account/tests/e2e/defaults/main.test.bicep @@ -20,8 +20,8 @@ param serviceShort string = 'pvamin' param namePrefix string = '#_namePrefix_#' // Set to fixed location as the RP function returns unsupported locations -// Right now (2024/03) the following locations are supported: eastus, eastus2, southcentralus, westcentralus, westus, westus2, westus3 -param enforcedLocation string = 'eastus' +// Right now (2024/07) the following locations are supported: uksouth +param enforcedLocation string = 'uksouth' // =========== // // Deployments // diff --git a/avm/res/purview/account/tests/e2e/max/main.test.bicep b/avm/res/purview/account/tests/e2e/max/main.test.bicep index bd88978cbe..d50eef4117 100644 --- a/avm/res/purview/account/tests/e2e/max/main.test.bicep +++ b/avm/res/purview/account/tests/e2e/max/main.test.bicep @@ -20,8 +20,8 @@ param serviceShort string = 'pvamax' param namePrefix string = '#_namePrefix_#' // Set to fixed location as the RP function returns unsupported locations -// Right now (2024/03) the following locations are supported: eastus, eastus2, southcentralus, westcentralus, westus, westus2, westus3 -param enforcedLocation string = 'eastus' +// Right now (2024/07) the following locations are supported: uksouth +param enforcedLocation string = 'uksouth' // =========== // // Deployments // @@ -99,11 +99,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '8372742c-408e-4a8a-a748-aca787a0e33e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -119,9 +121,13 @@ module testDeployment '../../../main.bicep' = [ ] accountPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.purviewAccountPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.purviewAccountPrivateDNSResourceId + } + ] + } service: 'account' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -133,9 +139,13 @@ module testDeployment '../../../main.bicep' = [ ] portalPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.purviewPortalPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.purviewPortalPrivateDNSResourceId + } + ] + } service: 'portal' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -145,17 +155,25 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.purviewPortalPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.purviewPortalPrivateDNSResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] storageBlobPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.storageBlobPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.storageBlobPrivateDNSResourceId + } + ] + } service: 'blob' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -167,9 +185,13 @@ module testDeployment '../../../main.bicep' = [ ] storageQueuePrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.storageQueuePrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.storageQueuePrivateDNSResourceId + } + ] + } service: 'queue' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -179,17 +201,25 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.storageQueuePrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.storageQueuePrivateDNSResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] eventHubPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.eventHubPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.eventHubPrivateDNSResourceId + } + ] + } service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { diff --git a/avm/res/purview/account/tests/e2e/waf-aligned/main.test.bicep b/avm/res/purview/account/tests/e2e/waf-aligned/main.test.bicep index b42f8a72fc..dbf48b0d92 100644 --- a/avm/res/purview/account/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/purview/account/tests/e2e/waf-aligned/main.test.bicep @@ -20,8 +20,8 @@ param serviceShort string = 'pvawaf' param namePrefix string = '#_namePrefix_#' // Set to fixed location as the RP function returns unsupported locations -// Right now (2024/03) the following locations are supported: eastus, eastus2, southcentralus, westcentralus, westus, westus2, westus3 -param enforcedLocation string = 'eastus' +// Right now (2024/07) the following locations are supported: uksouth +param enforcedLocation string = 'uksouth' // =========== // // Deployments // @@ -87,45 +87,65 @@ module testDeployment '../../../main.bicep' = [ ] accountPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.purviewAccountPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.purviewAccountPrivateDNSResourceId + } + ] + } service: 'account' subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] portalPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.purviewPortalPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.purviewPortalPrivateDNSResourceId + } + ] + } service: 'portal' subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] storageBlobPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.storageBlobPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.storageBlobPrivateDNSResourceId + } + ] + } service: 'blob' subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] storageQueuePrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.storageQueuePrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.storageQueuePrivateDNSResourceId + } + ] + } service: 'queue' subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] eventHubPrivateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.eventHubPrivateDNSResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.eventHubPrivateDNSResourceId + } + ] + } service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId } diff --git a/avm/res/purview/account/version.json b/avm/res/purview/account/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/purview/account/version.json +++ b/avm/res/purview/account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/recovery-services/vault/README.md b/avm/res/recovery-services/vault/README.md index bf015792f0..f0756aa9ab 100644 --- a/avm/res/recovery-services/vault/README.md +++ b/avm/res/recovery-services/vault/README.md @@ -18,8 +18,8 @@ This module deploys a Recovery Services Vault. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.RecoveryServices/vaults` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults) | | `Microsoft.RecoveryServices/vaults/backupconfig` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupconfig) | | `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers) | @@ -30,7 +30,7 @@ This module deploys a Recovery Services Vault. | `Microsoft.RecoveryServices/vaults/replicationFabrics` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-10-01/vaults/replicationFabrics) | | `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-10-01/vaults/replicationFabrics/replicationProtectionContainers) | | `Microsoft.RecoveryServices/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2022-10-01/vaults/replicationFabrics/replicationProtectionContainers/replicationProtectionContainerMappings) | -| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2023-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/vaults/replicationPolicies) | +| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2023-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-06-01/vaults/replicationPolicies) | ## Usage examples @@ -626,9 +626,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -646,11 +650,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } roleAssignments: [ { + name: '35288372-e6b4-4333-9ee6-dd997b96d52b' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1024,9 +1030,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -1048,11 +1058,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { "roleAssignments": { "value": [ { + "name": "35288372-e6b4-4333-9ee6-dd997b96d52b", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1416,9 +1428,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -1797,9 +1813,13 @@ module vault 'br/public:avm/res/recovery-services/vault:' = { } } ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -2163,8 +2183,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -2348,19 +2367,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The private DNS zone group to configure for the private endpoint. - Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the private DNS zone group config. - Required: No -- Type: array +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -2398,6 +2462,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -2448,6 +2513,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2548,6 +2620,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2598,6 +2671,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2637,6 +2717,7 @@ Tags of the Recovery Service Vault resource. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The Name of the recovery services vault. | +| `privateEndpoints` | array | The private endpoints of the recovery services vault. | | `resourceGroupName` | string | The name of the resource group the recovery services vault was created in. | | `resourceId` | string | The resource ID of the recovery services vault. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -2647,7 +2728,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/recovery-services/vault/backup-config/main.json b/avm/res/recovery-services/vault/backup-config/main.json index e073b21316..acb2cb784e 100644 --- a/avm/res/recovery-services/vault/backup-config/main.json +++ b/avm/res/recovery-services/vault/backup-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1357823955889928306" + "version": "0.29.47.4906", + "templateHash": "16706009499329717813" }, "name": "Recovery Services Vault Backup Config", "description": "This module deploys a Recovery Services Vault Backup Config.", diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json b/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json index 355932d886..b85b737445 100644 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json +++ b/avm/res/recovery-services/vault/backup-fabric/protection-container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "869858461413697915" + "version": "0.29.47.4906", + "templateHash": "7107776981403889232" }, "name": "Recovery Services Vault Protection Container", "description": "This module deploys a Recovery Services Vault Protection Container.", @@ -144,8 +144,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4311417160342913977" + "version": "0.29.47.4906", + "templateHash": "4676289166883418684" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", diff --git a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json index 778d2d182b..f337cc9f66 100644 --- a/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json +++ b/avm/res/recovery-services/vault/backup-fabric/protection-container/protected-item/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4311417160342913977" + "version": "0.29.47.4906", + "templateHash": "4676289166883418684" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", diff --git a/avm/res/recovery-services/vault/backup-policy/main.json b/avm/res/recovery-services/vault/backup-policy/main.json index 06cff15670..64efc19007 100644 --- a/avm/res/recovery-services/vault/backup-policy/main.json +++ b/avm/res/recovery-services/vault/backup-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14655418559802778647" + "version": "0.29.47.4906", + "templateHash": "3484552475575852642" }, "name": "Recovery Services Vault Backup Policies", "description": "This module deploys a Recovery Services Vault Backup Policy.", diff --git a/avm/res/recovery-services/vault/backup-storage-config/main.json b/avm/res/recovery-services/vault/backup-storage-config/main.json index 84ab163c22..d4257df69b 100644 --- a/avm/res/recovery-services/vault/backup-storage-config/main.json +++ b/avm/res/recovery-services/vault/backup-storage-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15994175444677989305" + "version": "0.29.47.4906", + "templateHash": "15771571961904697298" }, "name": "Recovery Services Vault Backup Storage Config", "description": "This module deploys a Recovery Service Vault Backup Storage Configuration.", diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 8ed3100ec1..5ee99722fa 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -116,6 +116,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.recoveryservices-vault.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -302,7 +313,7 @@ resource rsv_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-0 } ] -module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-rsv-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -343,8 +354,7 @@ module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -356,14 +366,10 @@ module rsv_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = ] resource rsv_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(rsv.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(rsv.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -390,6 +396,17 @@ output systemAssignedMIPrincipalId string = rsv.?identity.?principalId ?? '' @description('The location the resource was deployed into.') output location string = rsv.location +@description('The private endpoints of the recovery services vault.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: rsv_privateEndpoints[i].outputs.name + resourceId: rsv_privateEndpoints[i].outputs.resourceId + groupId: rsv_privateEndpoints[i].outputs.groupId + customDnsConfig: rsv_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: rsv_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -411,6 +428,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -449,11 +469,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index d60109dcb0..91ffe8c500 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10020429866432711440" + "version": "0.29.47.4906", + "templateHash": "1317340221517130136" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -579,6 +609,13 @@ } }, "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": { @@ -691,20 +728,20 @@ "rsv_roleAssignments": { "copy": { "name": "rsv_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.RecoveryServices/vaults/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.RecoveryServices/vaults', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.RecoveryServices/vaults', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "rsv" @@ -739,8 +776,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9853739797036762243" + "version": "0.29.47.4906", + "templateHash": "14351620305513283067" }, "name": "Recovery Services Vault Replication Fabrics", "description": "This module deploys a Replication Fabric for Azure to Azure disaster recovery scenario of Azure Site Recovery.\n\n> Note: this module currently support only the `instanceType: 'Azure'` scenario.", @@ -818,8 +855,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3376272434476190279" + "version": "0.29.47.4906", + "templateHash": "10179392610574660553" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", @@ -901,8 +938,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5064323509873410241" + "version": "0.29.47.4906", + "templateHash": "9091386051733041821" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", @@ -1110,8 +1147,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12207853682447341911" + "version": "0.29.47.4906", + "templateHash": "14083088922027127333" }, "name": "Recovery Services Vault Replication Policies", "description": "This module deploys a Recovery Services Vault Replication Policy for Disaster Recovery scenario.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", @@ -1235,8 +1272,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15994175444677989305" + "version": "0.29.47.4906", + "templateHash": "15771571961904697298" }, "name": "Recovery Services Vault Backup Storage Config", "description": "This module deploys a Recovery Service Vault Backup Storage Configuration.", @@ -1361,8 +1398,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "869858461413697915" + "version": "0.29.47.4906", + "templateHash": "7107776981403889232" }, "name": "Recovery Services Vault Protection Container", "description": "This module deploys a Recovery Services Vault Protection Container.", @@ -1500,8 +1537,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4311417160342913977" + "version": "0.29.47.4906", + "templateHash": "4676289166883418684" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", @@ -1666,8 +1703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14655418559802778647" + "version": "0.29.47.4906", + "templateHash": "3484552475575852642" }, "name": "Recovery Services Vault Backup Policies", "description": "This module deploys a Recovery Services Vault Backup Policy.", @@ -1759,8 +1796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1357823955889928306" + "version": "0.29.47.4906", + "templateHash": "16706009499329717813" }, "name": "Recovery Services Vault Backup Config", "description": "This module deploys a Recovery Services Vault Backup Config.", @@ -1926,8 +1963,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12427854509640605854" + "version": "0.29.47.4906", + "templateHash": "14356935491527849801" }, "name": "Recovery Services Vault Replication Alert Settings", "description": "This module deploys a Recovery Services Vault Replication Alert Settings.", @@ -2046,11 +2083,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -2078,19 +2112,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2194,13 +2256,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -2234,8 +2296,11 @@ "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." + "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": { @@ -2275,8 +2340,11 @@ "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." + "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": { @@ -2324,6 +2392,29 @@ } }, "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": { @@ -2359,18 +2450,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "nullable": true, - "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2426,6 +2510,13 @@ } }, "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')]", @@ -2443,8 +2534,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2462,7 +2553,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2503,27 +2594,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -2534,28 +2625,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -2563,12 +2678,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -2582,27 +2700,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2660,14 +2787,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2712,6 +2853,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('rsv', '2023-01-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the recovery services vault." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('rsv_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/recovery-services/vault/replication-alert-setting/main.json b/avm/res/recovery-services/vault/replication-alert-setting/main.json index 1800c62179..ef2755c71e 100644 --- a/avm/res/recovery-services/vault/replication-alert-setting/main.json +++ b/avm/res/recovery-services/vault/replication-alert-setting/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12427854509640605854" + "version": "0.29.47.4906", + "templateHash": "14356935491527849801" }, "name": "Recovery Services Vault Replication Alert Settings", "description": "This module deploys a Recovery Services Vault Replication Alert Settings.", diff --git a/avm/res/recovery-services/vault/replication-fabric/main.json b/avm/res/recovery-services/vault/replication-fabric/main.json index 092588e7ff..62b645b48d 100644 --- a/avm/res/recovery-services/vault/replication-fabric/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9853739797036762243" + "version": "0.29.47.4906", + "templateHash": "14351620305513283067" }, "name": "Recovery Services Vault Replication Fabrics", "description": "This module deploys a Replication Fabric for Azure to Azure disaster recovery scenario of Azure Site Recovery.\n\n> Note: this module currently support only the `instanceType: 'Azure'` scenario.", @@ -83,8 +83,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3376272434476190279" + "version": "0.29.47.4906", + "templateHash": "10179392610574660553" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", @@ -166,8 +166,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5064323509873410241" + "version": "0.29.47.4906", + "templateHash": "9091386051733041821" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json index b91b77c050..691ec1374c 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3376272434476190279" + "version": "0.29.47.4906", + "templateHash": "10179392610574660553" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Containers", "description": "This module deploys a Recovery Services Vault Replication Protection Container.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", @@ -87,8 +87,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5064323509873410241" + "version": "0.29.47.4906", + "templateHash": "9091386051733041821" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", diff --git a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json index 6f45afb630..211070c849 100644 --- a/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json +++ b/avm/res/recovery-services/vault/replication-fabric/replication-protection-container/replication-protection-container-mapping/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5064323509873410241" + "version": "0.29.47.4906", + "templateHash": "9091386051733041821" }, "name": "Recovery Services Vault Replication Fabric Replication Protection Container Replication Protection Container Mappings", "description": "This module deploys a Recovery Services Vault (RSV) Replication Protection Container Mapping.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", diff --git a/avm/res/recovery-services/vault/replication-policy/README.md b/avm/res/recovery-services/vault/replication-policy/README.md index 67f1af8480..d54fc31cd4 100644 --- a/avm/res/recovery-services/vault/replication-policy/README.md +++ b/avm/res/recovery-services/vault/replication-policy/README.md @@ -16,7 +16,7 @@ This module deploys a Recovery Services Vault Replication Policy for Disaster Re | Resource Type | API Version | | :-- | :-- | -| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2023-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/vaults/replicationPolicies) | +| `Microsoft.RecoveryServices/vaults/replicationPolicies` | [2023-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-06-01/vaults/replicationPolicies) | ## Parameters diff --git a/avm/res/recovery-services/vault/replication-policy/main.json b/avm/res/recovery-services/vault/replication-policy/main.json index 2bd2c3e825..5e8f6eda48 100644 --- a/avm/res/recovery-services/vault/replication-policy/main.json +++ b/avm/res/recovery-services/vault/replication-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12207853682447341911" + "version": "0.29.47.4906", + "templateHash": "14083088922027127333" }, "name": "Recovery Services Vault Replication Policies", "description": "This module deploys a Recovery Services Vault Replication Policy for Disaster Recovery scenario.\n\n> **Note**: this version of the module only supports the `instanceType: 'A2A'` scenario.", diff --git a/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep index 109ccdfcfa..28e3b85435 100644 --- a/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/max/main.test.bicep @@ -380,9 +380,13 @@ module testDeployment '../../../main.bicep' = [ } } ] - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -393,11 +397,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '35288372-e6b4-4333-9ee6-dd997b96d52b' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep b/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep index 925915c278..7ad49f1ccb 100644 --- a/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/recovery-services/vault/tests/e2e/waf-aligned/main.test.bicep @@ -380,9 +380,13 @@ module testDeployment '../../../main.bicep' = [ } } ] - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' diff --git a/avm/res/recovery-services/vault/version.json b/avm/res/recovery-services/vault/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/recovery-services/vault/version.json +++ b/avm/res/recovery-services/vault/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/relay/namespace/README.md b/avm/res/relay/namespace/README.md index 00e11010ba..55d143ec68 100644 --- a/avm/res/relay/namespace/README.md +++ b/avm/res/relay/namespace/README.md @@ -23,8 +23,8 @@ This module deploys a Relay Namespace | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Relay/namespaces` | [2021-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Relay/2021-11-01/namespaces) | | `Microsoft.Relay/namespaces/authorizationRules` | [2021-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Relay/2021-11-01/namespaces/authorizationRules) | | `Microsoft.Relay/namespaces/hybridConnections` | [2021-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Relay/2021-11-01/namespaces/hybridConnections) | @@ -182,9 +182,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' tags: { @@ -194,19 +198,25 @@ module namespace 'br/public:avm/res/relay/namespace:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] roleAssignments: [ { + name: 'd3dff05a-96d7-4d63-82c2-0fd8ac7b859d' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -343,9 +353,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "", "tags": { @@ -355,9 +369,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -365,11 +383,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { "roleAssignments": { "value": [ { + "name": "d3dff05a-96d7-4d63-82c2-0fd8ac7b859d", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -485,9 +505,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' tags: { @@ -596,9 +620,13 @@ module namespace 'br/public:avm/res/relay/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "", "tags": { @@ -925,8 +953,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1110,19 +1137,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1160,6 +1232,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1210,6 +1283,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1263,6 +1343,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1313,6 +1394,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1366,6 +1454,7 @@ The wcf relays to create in the relay namespace. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the deployed relay namespace. | +| `privateEndpoints` | array | The private endpoints of the relay namespace. | | `resourceGroupName` | string | The resource group of the deployed relay namespace. | | `resourceId` | string | The resource ID of the deployed relay namespace. | @@ -1375,7 +1464,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/relay/namespace/authorization-rule/main.json b/avm/res/relay/namespace/authorization-rule/main.json index 6acf611107..1dcf45602b 100644 --- a/avm/res/relay/namespace/authorization-rule/main.json +++ b/avm/res/relay/namespace/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15785124500274397709" + "version": "0.29.47.4906", + "templateHash": "13234316448276715063" }, "name": "Relay Namespace Authorization Rules", "description": "This module deploys a Relay Namespace Authorization Rule.", diff --git a/avm/res/relay/namespace/hybrid-connection/README.md b/avm/res/relay/namespace/hybrid-connection/README.md index 942e7d5e26..54d62f2dc8 100644 --- a/avm/res/relay/namespace/hybrid-connection/README.md +++ b/avm/res/relay/namespace/hybrid-connection/README.md @@ -162,6 +162,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -212,6 +213,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/relay/namespace/hybrid-connection/authorization-rule/main.json b/avm/res/relay/namespace/hybrid-connection/authorization-rule/main.json index 40f9a1f479..01bb4c827b 100644 --- a/avm/res/relay/namespace/hybrid-connection/authorization-rule/main.json +++ b/avm/res/relay/namespace/hybrid-connection/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5917921495785006210" + "version": "0.29.47.4906", + "templateHash": "11780139902470137859" }, "name": "Hybrid Connection Authorization Rules", "description": "This module deploys a Hybrid Connection Authorization Rule.", diff --git a/avm/res/relay/namespace/hybrid-connection/main.bicep b/avm/res/relay/namespace/hybrid-connection/main.bicep index 92ce96e61a..6c31cfede6 100644 --- a/avm/res/relay/namespace/hybrid-connection/main.bicep +++ b/avm/res/relay/namespace/hybrid-connection/main.bicep @@ -74,6 +74,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource namespace 'Microsoft.Relay/namespaces@2021-11-01' existing = { name: namespaceName } @@ -111,14 +122,10 @@ resource hybridConnection_lock 'Microsoft.Authorization/locks@2020-05-01' = if ( } resource hybridConnection_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(hybridConnection.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(hybridConnection.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -152,6 +159,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/relay/namespace/hybrid-connection/main.json b/avm/res/relay/namespace/hybrid-connection/main.json index bd069eb16b..219af4e2d5 100644 --- a/avm/res/relay/namespace/hybrid-connection/main.json +++ b/avm/res/relay/namespace/hybrid-connection/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8380211089689933912" + "version": "0.29.47.4906", + "templateHash": "6090434662270085474" }, "name": "Relay Namespace Hybrid Connections", "description": "This module deploys a Relay Namespace Hybrid Connection.", @@ -43,6 +43,13 @@ "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": { @@ -177,6 +184,13 @@ } }, "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": { "Azure Relay Listener": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '26e0b698-aa6d-4085-9386-aadae190014d')]", "Azure Relay Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2787bf04-f1f5-4bfe-8383-c8a24483ee38')]", @@ -224,20 +238,20 @@ "hybridConnection_roleAssignments": { "copy": { "name": "hybridConnection_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Relay/namespaces/{0}/hybridConnections/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Relay/namespaces/hybridConnections', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Relay/namespaces/hybridConnections', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "hybridConnection" @@ -276,8 +290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5917921495785006210" + "version": "0.29.47.4906", + "templateHash": "11780139902470137859" }, "name": "Hybrid Connection Authorization Rules", "description": "This module deploys a Hybrid Connection Authorization Rule.", diff --git a/avm/res/relay/namespace/main.bicep b/avm/res/relay/namespace/main.bicep index 0a4a6ffa93..e28a037e51 100644 --- a/avm/res/relay/namespace/main.bicep +++ b/avm/res/relay/namespace/main.bicep @@ -81,6 +81,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.relay-namespace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -253,7 +264,7 @@ resource namespace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@202 } ] -module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-namespace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -294,8 +305,7 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -307,14 +317,10 @@ module namespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. ] resource namespace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(namespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(namespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -325,6 +331,7 @@ resource namespace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 scope: namespace } ] + @description('The resource ID of the deployed relay namespace.') output resourceId string = namespace.id @@ -337,6 +344,17 @@ output name string = namespace.name @description('The location the resource was deployed into.') output location string = namespace.location +@description('The private endpoints of the relay namespace.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: namespace_privateEndpoints[i].outputs.name + resourceId: namespace_privateEndpoints[i].outputs.resourceId + groupId: namespace_privateEndpoints[i].outputs.groupId + customDnsConfig: namespace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: namespace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -350,6 +368,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -388,11 +409,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/relay/namespace/main.json b/avm/res/relay/namespace/main.json index 84c057c76b..66c0fbf16e 100644 --- a/avm/res/relay/namespace/main.json +++ b/avm/res/relay/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10652646803152318384" + "version": "0.29.47.4906", + "templateHash": "10535689791446770792" }, "name": "Relay Namespaces", "description": "This module deploys a Relay Namespace", @@ -43,6 +43,13 @@ "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": { @@ -143,21 +150,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -525,6 +555,13 @@ } }, "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": { "Azure Relay Listener": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '26e0b698-aa6d-4085-9386-aadae190014d')]", "Azure Relay Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2787bf04-f1f5-4bfe-8383-c8a24483ee38')]", @@ -626,20 +663,20 @@ "namespace_roleAssignments": { "copy": { "name": "namespace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Relay/namespaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Relay/namespaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Relay/namespaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "namespace" @@ -673,8 +710,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15785124500274397709" + "version": "0.29.47.4906", + "templateHash": "13234316448276715063" }, "name": "Relay Namespace Authorization Rules", "description": "This module deploys a Relay Namespace Authorization Rule.", @@ -772,8 +809,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4867810584122567054" + "version": "0.29.47.4906", + "templateHash": "13158990350519912635" }, "name": "Relay Namespace Network Rules Sets", "description": "This module deploys a Relay Namespace Network Rule Set.", @@ -901,8 +938,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8380211089689933912" + "version": "0.29.47.4906", + "templateHash": "6090434662270085474" }, "name": "Relay Namespace Hybrid Connections", "description": "This module deploys a Relay Namespace Hybrid Connection.", @@ -939,6 +976,13 @@ "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": { @@ -1073,6 +1117,13 @@ } }, "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": { "Azure Relay Listener": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '26e0b698-aa6d-4085-9386-aadae190014d')]", "Azure Relay Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2787bf04-f1f5-4bfe-8383-c8a24483ee38')]", @@ -1120,20 +1171,20 @@ "hybridConnection_roleAssignments": { "copy": { "name": "hybridConnection_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Relay/namespaces/{0}/hybridConnections/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Relay/namespaces/hybridConnections', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Relay/namespaces/hybridConnections', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "hybridConnection" @@ -1172,8 +1223,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5917921495785006210" + "version": "0.29.47.4906", + "templateHash": "11780139902470137859" }, "name": "Hybrid Connection Authorization Rules", "description": "This module deploys a Hybrid Connection Authorization Rule.", @@ -1315,8 +1366,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14779566755232502715" + "version": "0.29.47.4906", + "templateHash": "4256310187824299728" }, "name": "Relay Namespace WCF Relays", "description": "This module deploys a Relay Namespace WCF Relay.", @@ -1353,6 +1404,13 @@ "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": { @@ -1505,6 +1563,13 @@ } }, "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": { "Azure Relay Listener": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '26e0b698-aa6d-4085-9386-aadae190014d')]", "Azure Relay Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2787bf04-f1f5-4bfe-8383-c8a24483ee38')]", @@ -1554,20 +1619,20 @@ "wcfRelay_roleAssignments": { "copy": { "name": "wcfRelay_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Relay/namespaces/{0}/wcfRelays/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Relay/namespaces/wcfRelays', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Relay/namespaces/wcfRelays', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "wcfRelay" @@ -1606,8 +1671,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3054703452311403412" + "version": "0.29.47.4906", + "templateHash": "9846145731132177305" }, "name": "WCF Relay Authorization Rules", "description": "This module deploys a WCF Relay Authorization Rule.", @@ -1746,11 +1811,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -1778,19 +1840,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -1894,13 +1984,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1934,8 +2024,11 @@ "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." + "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": { @@ -1975,8 +2068,11 @@ "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." + "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": { @@ -2024,6 +2120,29 @@ } }, "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": { @@ -2059,18 +2178,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -2126,6 +2238,13 @@ } }, "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')]", @@ -2143,8 +2262,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2162,7 +2281,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2203,27 +2322,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -2234,28 +2353,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -2263,12 +2406,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -2282,27 +2428,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -2360,14 +2515,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2405,6 +2574,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('namespace', '2021-11-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the relay namespace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('namespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/relay/namespace/network-rule-set/main.json b/avm/res/relay/namespace/network-rule-set/main.json index 8ba3db6f66..3af1bab1b5 100644 --- a/avm/res/relay/namespace/network-rule-set/main.json +++ b/avm/res/relay/namespace/network-rule-set/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4867810584122567054" + "version": "0.29.47.4906", + "templateHash": "13158990350519912635" }, "name": "Relay Namespace Network Rules Sets", "description": "This module deploys a Relay Namespace Network Rule Set.", diff --git a/avm/res/relay/namespace/tests/e2e/max/main.test.bicep b/avm/res/relay/namespace/tests/e2e/max/main.test.bicep index 4349a7c946..687c8e3487 100644 --- a/avm/res/relay/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/relay/namespace/tests/e2e/max/main.test.bicep @@ -79,11 +79,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: 'd3dff05a-96d7-4d63-82c2-0fd8ac7b859d' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -180,9 +182,13 @@ module testDeployment '../../../main.bicep' = [ { service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -191,9 +197,13 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] } diff --git a/avm/res/relay/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/relay/namespace/tests/e2e/waf-aligned/main.test.bicep index 4d8538975c..99ae681c88 100644 --- a/avm/res/relay/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/relay/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -134,9 +134,13 @@ module testDeployment '../../../main.bicep' = [ { service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/relay/namespace/version.json b/avm/res/relay/namespace/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/relay/namespace/version.json +++ b/avm/res/relay/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/relay/namespace/wcf-relay/README.md b/avm/res/relay/namespace/wcf-relay/README.md index 99816c975d..935e651364 100644 --- a/avm/res/relay/namespace/wcf-relay/README.md +++ b/avm/res/relay/namespace/wcf-relay/README.md @@ -179,6 +179,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -229,6 +230,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/relay/namespace/wcf-relay/authorization-rule/main.json b/avm/res/relay/namespace/wcf-relay/authorization-rule/main.json index 6c9110e3fc..a864b5d569 100644 --- a/avm/res/relay/namespace/wcf-relay/authorization-rule/main.json +++ b/avm/res/relay/namespace/wcf-relay/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3054703452311403412" + "version": "0.29.47.4906", + "templateHash": "9846145731132177305" }, "name": "WCF Relay Authorization Rules", "description": "This module deploys a WCF Relay Authorization Rule.", diff --git a/avm/res/relay/namespace/wcf-relay/authorization-rule/version.json b/avm/res/relay/namespace/wcf-relay/authorization-rule/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/relay/namespace/wcf-relay/authorization-rule/version.json +++ b/avm/res/relay/namespace/wcf-relay/authorization-rule/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/relay/namespace/wcf-relay/main.bicep b/avm/res/relay/namespace/wcf-relay/main.bicep index 6261c47d43..d38ae8a614 100644 --- a/avm/res/relay/namespace/wcf-relay/main.bicep +++ b/avm/res/relay/namespace/wcf-relay/main.bicep @@ -84,6 +84,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource namespace 'Microsoft.Relay/namespaces@2021-11-01' existing = { name: namespaceName } @@ -123,14 +134,10 @@ resource wcfRelay_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(l } resource wcfRelay_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(wcfRelay.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(wcfRelay.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -164,6 +171,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/relay/namespace/wcf-relay/main.json b/avm/res/relay/namespace/wcf-relay/main.json index bfde8b05dd..f5db122683 100644 --- a/avm/res/relay/namespace/wcf-relay/main.json +++ b/avm/res/relay/namespace/wcf-relay/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14779566755232502715" + "version": "0.29.47.4906", + "templateHash": "4256310187824299728" }, "name": "Relay Namespace WCF Relays", "description": "This module deploys a Relay Namespace WCF Relay.", @@ -43,6 +43,13 @@ "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": { @@ -195,6 +202,13 @@ } }, "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": { "Azure Relay Listener": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '26e0b698-aa6d-4085-9386-aadae190014d')]", "Azure Relay Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2787bf04-f1f5-4bfe-8383-c8a24483ee38')]", @@ -244,20 +258,20 @@ "wcfRelay_roleAssignments": { "copy": { "name": "wcfRelay_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Relay/namespaces/{0}/wcfRelays/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Relay/namespaces/wcfRelays', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Relay/namespaces/wcfRelays', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "wcfRelay" @@ -296,8 +310,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3054703452311403412" + "version": "0.29.47.4906", + "templateHash": "9846145731132177305" }, "name": "WCF Relay Authorization Rules", "description": "This module deploys a WCF Relay Authorization Rule.", diff --git a/avm/res/resource-graph/query/README.md b/avm/res/resource-graph/query/README.md index 067f0bec33..1f052dd1e4 100644 --- a/avm/res/resource-graph/query/README.md +++ b/avm/res/resource-graph/query/README.md @@ -107,11 +107,13 @@ module query 'br/public:avm/res/resource-graph/query:' = { queryDescription: 'An example query to list first 5 subscriptions.' roleAssignments: [ { + name: '9634350c-b241-4481-8c22-4166891596ab' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -165,11 +167,13 @@ module query 'br/public:avm/res/resource-graph/query:' = { "roleAssignments": { "value": [ { + "name": "9634350c-b241-4481-8c22-4166891596ab", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -388,6 +392,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -438,6 +443,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/resource-graph/query/main.bicep b/avm/res/resource-graph/query/main.bicep index b1e218aa7b..3f02f3f7ee 100644 --- a/avm/res/resource-graph/query/main.bicep +++ b/avm/res/resource-graph/query/main.bicep @@ -48,6 +48,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ================ // // Resources // // ================ // @@ -93,14 +104,10 @@ resource rgQuery 'Microsoft.ResourceGraph/queries@2018-09-01-preview' = { } resource rgQuery_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(rgQuery.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(rgQuery.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -141,6 +148,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/resource-graph/query/main.json b/avm/res/resource-graph/query/main.json index 0d69d95565..9ba837cb40 100644 --- a/avm/res/resource-graph/query/main.json +++ b/avm/res/resource-graph/query/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14486708619639683279" + "version": "0.29.47.4906", + "templateHash": "2991554121224170850" }, "name": "Resource Graph Queries", "description": "This module deploys a Resource Graph Query.", @@ -43,6 +43,13 @@ "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": { @@ -160,6 +167,13 @@ } }, "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')]", @@ -217,20 +231,20 @@ "rgQuery_roleAssignments": { "copy": { "name": "rgQuery_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ResourceGraph/queries/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ResourceGraph/queries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ResourceGraph/queries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "rgQuery" diff --git a/avm/res/resource-graph/query/tests/e2e/max/main.test.bicep b/avm/res/resource-graph/query/tests/e2e/max/main.test.bicep index d5075817e7..f27a4eca40 100644 --- a/avm/res/resource-graph/query/tests/e2e/max/main.test.bicep +++ b/avm/res/resource-graph/query/tests/e2e/max/main.test.bicep @@ -55,11 +55,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: '9634350c-b241-4481-8c22-4166891596ab' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/resource-graph/query/version.json b/avm/res/resource-graph/query/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/resource-graph/query/version.json +++ b/avm/res/resource-graph/query/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/resources/deployment-script/README.md b/avm/res/resources/deployment-script/README.md index d795e9babe..ca296e1532 100644 --- a/avm/res/resources/deployment-script/README.md +++ b/avm/res/resources/deployment-script/README.md @@ -30,9 +30,10 @@ The following section provides usage examples for the module, which were used to - [Using Azure CLI](#example-1-using-azure-cli) - [Using only defaults](#example-2-using-only-defaults) - [Using large parameter set](#example-3-using-large-parameter-set) -- [Using Private Networking](#example-4-using-private-networking) -- [Using Azure PowerShell](#example-5-using-azure-powershell) -- [WAF-aligned](#example-6-waf-aligned) +- [Using Private Endpoint](#example-4-using-private-endpoint) +- [Using Private Networking](#example-5-using-private-networking) +- [Using Azure PowerShell](#example-6-using-azure-powershell) +- [WAF-aligned](#example-7-waf-aligned) ### Example 1: _Using Azure CLI_ @@ -52,14 +53,12 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: name: 'rdscli001' // Non-required parameters azCliVersion: '2.9.1' - environmentVariables: { - secureList: [ - { - name: 'var1' - value: 'AVM Deployment Script test!' - } - ] - } + environmentVariables: [ + { + name: 'var1' + value: 'AVM Deployment Script test!' + } + ] location: '' managedIdentities: { userAssignedResourcesIds: [ @@ -97,14 +96,12 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: "value": "2.9.1" }, "environmentVariables": { - "value": { - "secureList": [ - { - "name": "var1", - "value": "AVM Deployment Script test!" - } - ] - } + "value": [ + { + "name": "var1", + "value": "AVM Deployment Script test!" + } + ] }, "location": { "value": "" @@ -225,18 +222,16 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: azCliVersion: '2.9.1' cleanupPreference: 'Always' containerGroupName: 'dep-cg-rdsmax' - environmentVariables: { - secureList: [ - { - name: 'var1' - value: 'test' - } - { - name: 'var2' - secureValue: '' - } - ] - } + environmentVariables: [ + { + name: 'var1' + value: 'test' + } + { + name: 'var2' + secureValue: '' + } + ] location: '' lock: { kind: 'None' @@ -249,11 +244,13 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: retentionInterval: 'P1D' roleAssignments: [ { + name: 'd8eadbae-2c20-4e8f-9a48-4c6d739d0c4a' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -310,18 +307,16 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: "value": "dep-cg-rdsmax" }, "environmentVariables": { - "value": { - "secureList": [ - { - "name": "var1", - "value": "test" - }, - { - "name": "var2", - "secureValue": "" - } - ] - } + "value": [ + { + "name": "var1", + "value": "test" + }, + { + "name": "var2", + "secureValue": "" + } + ] }, "location": { "value": "" @@ -344,11 +339,13 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: "roleAssignments": { "value": [ { + "name": "d8eadbae-2c20-4e8f-9a48-4c6d739d0c4a", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -386,7 +383,107 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script:

-### Example 4: _Using Private Networking_ +### Example 4: _Using Private Endpoint_ + +This instance deploys the module with access to a private endpoint. + + +

+ +via Bicep module + +```bicep +module deploymentScript 'br/public:avm/res/resources/deployment-script:' = { + name: 'deploymentScriptDeployment' + params: { + // Required parameters + kind: 'AzureCLI' + name: 'rdspe001' + // Non-required parameters + azCliVersion: '2.9.1' + cleanupPreference: 'Always' + location: '' + managedIdentities: { + userAssignedResourcesIds: [ + '' + ] + } + retentionInterval: 'P1D' + runOnce: true + scriptContent: 'echo \'AVM Deployment Script test!\'' + storageAccountResourceId: '' + subnetResourceIds: [ + '' + ] + timeout: 'PT1H' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "AzureCLI" + }, + "name": { + "value": "rdspe001" + }, + // Non-required parameters + "azCliVersion": { + "value": "2.9.1" + }, + "cleanupPreference": { + "value": "Always" + }, + "location": { + "value": "" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "" + ] + } + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": true + }, + "scriptContent": { + "value": "echo \"AVM Deployment Script test!\"" + }, + "storageAccountResourceId": { + "value": "" + }, + "subnetResourceIds": { + "value": [ + "" + ] + }, + "timeout": { + "value": "PT1H" + } + } +} +``` + +
+

+ +### Example 5: _Using Private Networking_ This instance deploys the module with access to a private network. @@ -486,7 +583,7 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script:

-### Example 5: _Using Azure PowerShell_ +### Example 6: _Using Azure PowerShell_ This instance deploys the module with an Azure PowerShell script. @@ -570,7 +667,7 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script:

-### Example 6: _WAF-aligned_ +### Example 7: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Well-Architected Framework. @@ -702,7 +799,7 @@ module deploymentScript 'br/public:avm/res/resources/deployment-script: | [`cleanupPreference`](#parameter-cleanuppreference) | string | The clean up preference when the script execution gets in a terminal state. Specify the preference on when to delete the deployment script resources. The default value is Always, which means the deployment script resources are deleted despite the terminal state (Succeeded, Failed, canceled). | | [`containerGroupName`](#parameter-containergroupname) | string | Container group name, if not specified then the name will get auto-generated. Not specifying a 'containerGroupName' indicates the system to generate a unique name which might end up flagging an Azure Policy as non-compliant. Use 'containerGroupName' when you have an Azure Policy that expects a specific naming convention or when you want to fully control the name. 'containerGroupName' property must be between 1 and 63 characters long, must contain only lowercase letters, numbers, and dashes and it cannot start or end with a dash and consecutive dashes are not allowed. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`environmentVariables`](#parameter-environmentvariables) | secureObject | The environment variables to pass over to the script. The list is passed as an object with a key name "secureList" and the value is the list of environment variables (array). The list must have a 'name' and a 'value' or a 'secretValue' property for each object. | +| [`environmentVariables`](#parameter-environmentvariables) | array | The environment variables to pass over to the script. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | @@ -798,10 +895,39 @@ Enable/Disable usage telemetry for module. ### Parameter: `environmentVariables` -The environment variables to pass over to the script. The list is passed as an object with a key name "secureList" and the value is the list of environment variables (array). The list must have a 'name' and a 'value' or a 'secretValue' property for each object. +The environment variables to pass over to the script. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-environmentvariablesname) | string | The name of the environment variable. | +| [`secureValue`](#parameter-environmentvariablessecurevalue) | securestring | The value of the secure environment variable. | +| [`value`](#parameter-environmentvariablesvalue) | string | The value of the environment variable. | + +### Parameter: `environmentVariables.name` + +The name of the environment variable. + +- Required: Yes +- Type: string + +### Parameter: `environmentVariables.secureValue` + +The value of the secure environment variable. - Required: No -- Type: secureObject +- Type: securestring + +### Parameter: `environmentVariables.value` + +The value of the environment variable. + +- Required: No +- Type: string ### Parameter: `location` @@ -904,6 +1030,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -954,6 +1081,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/resources/deployment-script/main.bicep b/avm/res/resources/deployment-script/main.bicep index 79114fecc9..55dc0612e2 100644 --- a/avm/res/resources/deployment-script/main.bicep +++ b/avm/res/resources/deployment-script/main.bicep @@ -37,8 +37,8 @@ param scriptContent string? @description('Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent parameter instead.') param primaryScriptUri string? -@description('Optional. The environment variables to pass over to the script. The list is passed as an object with a key name "secureList" and the value is the list of environment variables (array). The list must have a \'name\' and a \'value\' or a \'secretValue\' property for each object.') -param environmentVariables environmentVariableType +@description('Optional. The environment variables to pass over to the script.') +param environmentVariables environmentVariableType[]? @description('Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent).') param supportingScriptUris array? @@ -102,6 +102,17 @@ var builtInRoleNames = { ) } +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 subnetIds = [ for subnetResourceId in (subnetResourceIds ?? []): { id: subnetResourceId @@ -176,7 +187,7 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = { containerSettings: !empty(containerSettings) ? containerSettings : null storageAccountSettings: !empty(storageAccountResourceId) ? storageAccountSettings : null arguments: arguments - environmentVariables: environmentVariables != null ? environmentVariables!.secureList : [] + environmentVariables: environmentVariables scriptContent: !empty(scriptContent) ? scriptContent : null primaryScriptUri: !empty(primaryScriptUri) ? primaryScriptUri : null supportingScriptUris: !empty(supportingScriptUris) ? supportingScriptUris : null @@ -199,14 +210,10 @@ resource deploymentScript_lock 'Microsoft.Authorization/locks@2020-05-01' = if ( } resource deploymentScript_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(deploymentScript.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(deploymentScript.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -240,7 +247,7 @@ output name string = deploymentScript.name output location string = deploymentScript.location @description('The output of the deployment script.') -output outputs object = contains(deploymentScript.properties, 'outputs') ? deploymentScript.properties.outputs : {} +output outputs object = deploymentScript.properties.?outputs ?? {} @description('The logs of the deployment script.') output deploymentScriptLogs string[] = split(deploymentScriptLogs.properties.log, '\n') @@ -263,6 +270,9 @@ type managedIdentitiesType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -285,12 +295,14 @@ type roleAssignmentType = { delegatedManagedIdentityResourceId: string? }[]? -@secure() type environmentVariableType = { - @description('Optional. The list of environment variables to pass over to the deployment script.') - secureList: { - name: string - secureValue: string? - value: string? - }[] -}? + @description('Required. The name of the environment variable.') + name: string + + @description('Required. The value of the secure environment variable.') + @secure() + secureValue: string? + + @description('Required. The value of the environment variable.') + value: string? +} diff --git a/avm/res/resources/deployment-script/main.json b/avm/res/resources/deployment-script/main.json index 10ca6bb162..96f10eba58 100644 --- a/avm/res/resources/deployment-script/main.json +++ b/avm/res/resources/deployment-script/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15867169553418925622" + "version": "0.29.47.4906", + "templateHash": "11095024633645020544" }, "name": "Deployment Scripts", "description": "This module deploys Deployment Scripts.", @@ -58,6 +58,13 @@ "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": { @@ -120,32 +127,29 @@ "nullable": true }, "environmentVariableType": { - "type": "secureObject", + "type": "object", "properties": { - "secureList": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "secureValue": { - "type": "string", - "nullable": true - }, - "value": { - "type": "string", - "nullable": true - } - } - }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The list of environment variables to pass over to the deployment script." + "description": "Required. The value of the environment variable." } } - }, - "nullable": true + } } }, "parameters": { @@ -215,9 +219,13 @@ } }, "environmentVariables": { - "$ref": "#/definitions/environmentVariableType", + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, "metadata": { - "description": "Optional. The environment variables to pass over to the script. The list is passed as an object with a key name \"secureList\" and the value is the list of environment variables (array). The list must have a 'name' and a 'value' or a 'secretValue' property for each object." + "description": "Optional. The environment variables to pass over to the script." } }, "supportingScriptUris": { @@ -320,6 +328,11 @@ }, "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": "subnetIds", "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", @@ -386,7 +399,7 @@ "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", "arguments": "[parameters('arguments')]", - "environmentVariables": "[if(not(equals(parameters('environmentVariables'), null())), parameters('environmentVariables').secureList, createArray())]", + "environmentVariables": "[parameters('environmentVariables')]", "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", @@ -416,20 +429,20 @@ "deploymentScript_roleAssignments": { "copy": { "name": "deploymentScript_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "deploymentScript" @@ -479,7 +492,7 @@ "metadata": { "description": "The output of the deployment script." }, - "value": "[if(contains(reference('deploymentScript'), 'outputs'), reference('deploymentScript').outputs, createObject())]" + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" }, "deploymentScriptLogs": { "type": "array", diff --git a/avm/res/resources/deployment-script/tests/e2e/cli/main.test.bicep b/avm/res/resources/deployment-script/tests/e2e/cli/main.test.bicep index f2dc7b05ae..461b6f0409 100644 --- a/avm/res/resources/deployment-script/tests/e2e/cli/main.test.bicep +++ b/avm/res/resources/deployment-script/tests/e2e/cli/main.test.bicep @@ -54,14 +54,12 @@ module testDeployment '../../../main.bicep' = { azCliVersion: '2.9.1' kind: 'AzureCLI' retentionInterval: 'P1D' - environmentVariables: { - secureList: [ - { - name: 'var1' - value: 'AVM Deployment Script test!' - } - ] - } + environmentVariables: [ + { + name: 'var1' + value: 'AVM Deployment Script test!' + } + ] scriptContent: 'echo \'Enviornment variable value is: \' $var1' storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId managedIdentities: { diff --git a/avm/res/resources/deployment-script/tests/e2e/max/main.test.bicep b/avm/res/resources/deployment-script/tests/e2e/max/main.test.bicep index 9af8441a2c..5311631d44 100644 --- a/avm/res/resources/deployment-script/tests/e2e/max/main.test.bicep +++ b/avm/res/resources/deployment-script/tests/e2e/max/main.test.bicep @@ -65,18 +65,16 @@ module testDeployment '../../../main.bicep' = { } containerGroupName: 'dep-${namePrefix}-cg-${serviceShort}' arguments: '-argument1 \\"test\\"' - environmentVariables: { - secureList: [ - { - name: 'var1' - value: 'test' - } - { - name: 'var2' - secureValue: guid(deployment().name) - } - ] - } + environmentVariables: [ + { + name: 'var1' + value: 'test' + } + { + name: 'var2' + secureValue: guid(deployment().name) + } + ] managedIdentities: { userAssignedResourcesIds: [ nestedDependencies.outputs.managedIdentityResourceId @@ -84,11 +82,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: 'd8eadbae-2c20-4e8f-9a48-4c6d739d0c4a' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/resources/deployment-script/tests/e2e/private-endpoint/dependencies.bicep b/avm/res/resources/deployment-script/tests/e2e/private-endpoint/dependencies.bicep new file mode 100644 index 0000000000..6f3158fdda --- /dev/null +++ b/avm/res/resources/deployment-script/tests/e2e/private-endpoint/dependencies.bicep @@ -0,0 +1,154 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the private endpoint to create.') +param privateEndpointName string + +var addressPrefix = '10.0.0.0/16' + +// Role required for deployment script to be able to use a storage account via private networking +resource storageFileDataPrivilegedContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' + scope: tenant() +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource storageFileSharePermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('storageFileDataPrivilegedContributorRole', managedIdentity.id, storageAccount.id) + scope: storageAccount + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: storageFileDataPrivilegedContributor.id + principalType: 'ServicePrincipal' + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + publicNetworkAccess: 'Disabled' + allowSharedKeyAccess: true // Cannot be set to false when using a private network for the deployment script + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + } + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = { + name: privateEndpointName + location: location + properties: { + subnet: { + id: virtualNetwork.properties.subnets[1].id + } + privateLinkServiceConnections: [ + { + name: '${privateEndpointName}-storageConnection' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + 'file' + ] + } + } + ] + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.file.${environment().suffixes.storage}' + location: 'global' + + resource virtualNetworkLink 'virtualNetworkLinks' = { + name: uniqueString(virtualNetwork.name) + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } + } + + resource resRecord 'A' = { + name: storageAccount.name + properties: { + ttl: 10 + aRecords: [ + { + ipv4Address: first(first(privateEndpoint.properties.customDnsConfigs)!.ipAddresses) + } + ] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + delegations: [ + { + name: 'Microsoft.ContainerInstance.containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + } + { + name: 'peSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + } + } + ] + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created storage account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/resources/deployment-script/tests/e2e/private-endpoint/main.test.bicep b/avm/res/resources/deployment-script/tests/e2e/private-endpoint/main.test.bicep new file mode 100644 index 0000000000..67a1eb7402 --- /dev/null +++ b/avm/res/resources/deployment-script/tests/e2e/private-endpoint/main.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +metadata name = 'Using Private Endpoint' +metadata description = 'This instance deploys the module with access to a private endpoint.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'avm-${namePrefix}-resources.deploymentscripts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'rdspe' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}sa${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + privateEndpointName: 'dep-${namePrefix}-pe-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + azCliVersion: '2.9.1' + kind: 'AzureCLI' + retentionInterval: 'P1D' + cleanupPreference: 'Always' + subnetResourceIds: [ + nestedDependencies.outputs.subnetResourceId + ] + managedIdentities: { + userAssignedResourcesIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + timeout: 'PT1H' + runOnce: true + scriptContent: 'echo \'AVM Deployment Script test!\'' + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + } +} diff --git a/avm/res/resources/deployment-script/version.json b/avm/res/resources/deployment-script/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/resources/deployment-script/version.json +++ b/avm/res/resources/deployment-script/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/resources/resource-group/README.md b/avm/res/resources/resource-group/README.md index abbd41ebde..1a97e32df9 100644 --- a/avm/res/resources/resource-group/README.md +++ b/avm/res/resources/resource-group/README.md @@ -102,11 +102,13 @@ module resourceGroup 'br/public:avm/res/resources/resource-group:' = { } roleAssignments: [ { + name: '3566ddd3-870d-4618-bd22-3d50915a21ef' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -155,11 +157,13 @@ module resourceGroup 'br/public:avm/res/resources/resource-group:' = { "roleAssignments": { "value": [ { + "name": "3566ddd3-870d-4618-bd22-3d50915a21ef", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -355,6 +359,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -405,6 +410,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/resources/resource-group/main.bicep b/avm/res/resources/resource-group/main.bicep index 8c856199b2..b173bd9d03 100644 --- a/avm/res/resources/resource-group/main.bicep +++ b/avm/res/resources/resource-group/main.bicep @@ -89,6 +89,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/resources/resource-group/main.json b/avm/res/resources/resource-group/main.json index 8533417be9..d0a5b4e2a3 100644 --- a/avm/res/resources/resource-group/main.json +++ b/avm/res/resources/resource-group/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13111143124589719202" + "version": "0.29.47.4906", + "templateHash": "1112152378548482375" }, "name": "Resource Groups", "description": "This module deploys a Resource Group.", @@ -43,6 +43,13 @@ "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": { @@ -202,8 +209,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3101694150158706009" + "version": "0.29.47.4906", + "templateHash": "16740360506283584978" } }, "definitions": { @@ -288,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8717205010771951354" + "version": "0.29.47.4906", + "templateHash": "4236428492292165426" } }, "definitions": { @@ -298,6 +305,13 @@ "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": { @@ -369,6 +383,13 @@ } }, "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')]", @@ -386,19 +407,19 @@ "resourceGroup_roleAssignments": { "copy": { "name": "resourceGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "name": "[guid(resourceGroup().id, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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')]" } } } diff --git a/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep b/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep index 355d465e06..bf8bc903d4 100644 --- a/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep +++ b/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep @@ -4,30 +4,67 @@ param roleAssignments roleAssignmentType var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') - 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + 'Quota Request Operator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '0e5f05e5-9ab9-446b-b98d-1e2157c94125' + ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') - 'Template Spec Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c9b6475-caf0-4164-b5a1-2142a7116f4b') - 'Template Spec Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '392ae280-861d-42bd-9ea5-08ee6d83b80e') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Resource Policy Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '36243c78-bf99-498c-9df9-86d9f8d28608' + ) + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'Tag Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ) + 'Template Spec Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '1c9b6475-caf0-4164-b5a1-2142a7116f4b' + ) + 'Template Spec Reader': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '392ae280-861d-42bd-9ea5-08ee6d83b80e' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) } -resource resourceGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(resourceGroup().id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) - properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') ? roleAssignment.roleDefinitionIdOrName : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) - 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 +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)) + }) +] + +resource resourceGroup_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(resourceGroup().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 + } } -}] +] type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/resources/resource-group/tests/e2e/max/main.test.bicep b/avm/res/resources/resource-group/tests/e2e/max/main.test.bicep index 37a96bfff5..2e45ad530c 100644 --- a/avm/res/resources/resource-group/tests/e2e/max/main.test.bicep +++ b/avm/res/resources/resource-group/tests/e2e/max/main.test.bicep @@ -55,11 +55,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: '3566ddd3-870d-4618-bd22-3d50915a21ef' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/resources/resource-group/version.json b/avm/res/resources/resource-group/version.json index 9481fea58e..c177b1bb58 100644 --- a/avm/res/resources/resource-group/version.json +++ b/avm/res/resources/resource-group/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/search/search-service/README.md b/avm/res/search/search-service/README.md index 1bfbfab6a0..ecaf955671 100644 --- a/avm/res/search/search-service/README.md +++ b/avm/res/search/search-service/README.md @@ -18,8 +18,8 @@ This module deploys a Search Service. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Search/searchServices` | [2024-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Search/2024-03-01-preview/searchServices) | | `Microsoft.Search/searchServices/sharedPrivateLinkResources` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Search/2023-11-01/searchServices/sharedPrivateLinkResources) | @@ -147,11 +147,13 @@ module searchService 'br/public:avm/res/search/search-service:' = { replicaCount: 3 roleAssignments: [ { + name: '73ec30e0-2e25-475f-beec-d90cab332eb7' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -260,11 +262,13 @@ module searchService 'br/public:avm/res/search/search-service:' = { "roleAssignments": { "value": [ { + "name": "73ec30e0-2e25-475f-beec-d90cab332eb7", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -318,9 +322,13 @@ module searchService 'br/public:avm/res/search/search-service:' = { applicationSecurityGroupResourceIds: [ '' ] - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -328,13 +336,17 @@ module searchService 'br/public:avm/res/search/search-service:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] - publicNetworkAccess: 'disabled' + publicNetworkAccess: 'Disabled' sharedPrivateLinkResources: [ { groupId: 'blob' @@ -383,9 +395,13 @@ module searchService 'br/public:avm/res/search/search-service:' = { "applicationSecurityGroupResourceIds": [ "" ], - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -393,15 +409,19 @@ module searchService 'br/public:avm/res/search/search-service:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] }, "publicNetworkAccess": { - "value": "disabled" + "value": "Disabled" }, "sharedPrivateLinkResources": { "value": [ @@ -623,7 +643,7 @@ module searchService 'br/public:avm/res/search/search-service:' = { | [`networkRuleSet`](#parameter-networkruleset) | object | Network specific rules that determine how the Azure Cognitive Search service may be reached. | | [`partitionCount`](#parameter-partitioncount) | int | 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`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | -| [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | 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. | +| [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | 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. | | [`replicaCount`](#parameter-replicacount) | int | 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`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`semanticSearch`](#parameter-semanticsearch) | string | Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations. | @@ -954,8 +974,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1139,19 +1158,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1189,6 +1253,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1239,6 +1304,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1272,16 +1344,16 @@ Tags to be applied on all resources/resource groups in this deployment. ### Parameter: `publicNetworkAccess` -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. +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. - Required: No - Type: string -- Default: `'enabled'` +- Default: `'Enabled'` - Allowed: ```Bicep [ - 'disabled' - 'enabled' + 'Disabled' + 'Enabled' ] ``` @@ -1315,6 +1387,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1365,6 +1438,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1439,6 +1519,7 @@ Tags to help categorize the resource in the Azure portal. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the search service. | +| `privateEndpoints` | array | The private endpoints of the search service. | | `resourceGroupName` | string | The name of the resource group the search service was created in. | | `resourceId` | string | The resource ID of the search service. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -1449,7 +1530,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/search/search-service/main.bicep b/avm/res/search/search-service/main.bicep index 74531baff2..bc455a8f68 100644 --- a/avm/res/search/search-service/main.bicep +++ b/avm/res/search/search-service/main.bicep @@ -53,12 +53,12 @@ param privateEndpoints privateEndpointType @description('Optional. The sharedPrivateLinkResources to create as part of the search Service.') param sharedPrivateLinkResources array = [] -@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.') +@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.') @allowed([ - 'enabled' - 'disabled' + 'Enabled' + 'Disabled' ]) -param publicNetworkAccess string = 'enabled' +param publicNetworkAccess string = 'Enabled' @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.') @minValue(1) @@ -145,9 +145,20 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { - name: '46d3xbcp.search-searchservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + name: '46d3xbcp.res.search-searchservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' properties: { mode: 'Incremental' template: { @@ -182,7 +193,7 @@ resource searchService 'Microsoft.Search/searchServices@2024-03-01-preview' = { networkRuleSet: networkRuleSet partitionCount: partitionCount replicaCount: replicaCount - publicNetworkAccess: publicNetworkAccess + publicNetworkAccess: toLower(publicNetworkAccess) semanticSearch: semanticSearch } } @@ -228,14 +239,10 @@ resource searchService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!em } resource searchService_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(searchService.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(searchService.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -247,7 +254,7 @@ resource searchService_roleAssignments 'Microsoft.Authorization/roleAssignments@ } ] -module searchService_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module searchService_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-searchService-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -288,8 +295,7 @@ module searchService_privateEndpoints 'br/public:avm/res/network/private-endpoin 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -308,9 +314,7 @@ module searchService_sharedPrivateLinkResources 'shared-private-link-resource/ma for (sharedPrivateLinkResource, index) in sharedPrivateLinkResources: { name: '${uniqueString(deployment().name, location)}-searchService-SharedPrivateLink-${index}' params: { - name: contains(sharedPrivateLinkResource, 'name') - ? sharedPrivateLinkResource.name - : 'spl-${last(split(searchService.id, '/'))}-${sharedPrivateLinkResource.groupId}-${index}' + name: sharedPrivateLinkResource.?name ?? 'spl-${last(split(searchService.id, '/'))}-${sharedPrivateLinkResource.groupId}-${index}' searchServiceName: searchService.name privateLinkResourceId: sharedPrivateLinkResource.privateLinkResourceId groupId: sharedPrivateLinkResource.groupId @@ -339,6 +343,17 @@ output systemAssignedMIPrincipalId string = searchService.?identity.?principalId @description('The location the resource was deployed into.') output location string = searchService.location +@description('The private endpoints of the search service.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: searchService_privateEndpoints[i].outputs.name + resourceId: searchService_privateEndpoints[i].outputs.resourceId + groupId: searchService_privateEndpoints[i].outputs.groupId + customDnsConfig: searchService_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: searchService_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -360,6 +375,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -398,11 +416,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/search/search-service/main.json b/avm/res/search/search-service/main.json index 76c7b04490..04beacbf9e 100644 --- a/avm/res/search/search-service/main.json +++ b/avm/res/search/search-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5837464800906702498" + "version": "0.29.47.4906", + "templateHash": "10051405020131908010" }, "name": "Search Services", "description": "This module deploys a Search Service.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -540,13 +570,13 @@ }, "publicNetworkAccess": { "type": "string", - "defaultValue": "enabled", + "defaultValue": "Enabled", "allowedValues": [ - "enabled", - "disabled" + "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." + "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." } }, "replicaCount": { @@ -613,6 +643,13 @@ } }, "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', '')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -631,7 +668,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.search-searchservice.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.search-searchservice.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -667,7 +704,7 @@ "networkRuleSet": "[parameters('networkRuleSet')]", "partitionCount": "[parameters('partitionCount')]", "replicaCount": "[parameters('replicaCount')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "publicNetworkAccess": "[toLower(parameters('publicNetworkAccess'))]", "semanticSearch": "[parameters('semanticSearch')]" } }, @@ -729,20 +766,20 @@ "searchService_roleAssignments": { "copy": { "name": "searchService_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Search/searchServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -780,11 +817,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -812,19 +846,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -928,13 +990,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -968,8 +1030,11 @@ "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." + "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": { @@ -1009,8 +1074,11 @@ "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." + "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": { @@ -1058,6 +1126,29 @@ } }, "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": { @@ -1093,18 +1184,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1160,6 +1244,13 @@ } }, "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')]", @@ -1177,8 +1268,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1196,7 +1287,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1237,27 +1328,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1268,28 +1359,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1297,12 +1412,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1316,27 +1434,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1394,14 +1521,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1426,7 +1567,9 @@ }, "mode": "Incremental", "parameters": { - "name": "[if(contains(parameters('sharedPrivateLinkResources')[copyIndex()], 'name'), createObject('value', parameters('sharedPrivateLinkResources')[copyIndex()].name), createObject('value', format('spl-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), parameters('sharedPrivateLinkResources')[copyIndex()].groupId, copyIndex())))]", + "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')]" }, @@ -1450,8 +1593,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9318385911283347271" + "version": "0.29.47.4906", + "templateHash": "2330033720810948871" }, "name": "Search Services Private Link Resources", "description": "This module deploys a Search Service Private Link Resource.", @@ -1583,6 +1726,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('searchService', '2024-03-01-preview', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the search service." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(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": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/search/search-service/shared-private-link-resource/main.json b/avm/res/search/search-service/shared-private-link-resource/main.json index 434e7e87f6..ccb69cdd79 100644 --- a/avm/res/search/search-service/shared-private-link-resource/main.json +++ b/avm/res/search/search-service/shared-private-link-resource/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9318385911283347271" + "version": "0.29.47.4906", + "templateHash": "2330033720810948871" }, "name": "Search Services Private Link Resources", "description": "This module deploys a Search Service Private Link Resource.", diff --git a/avm/res/search/search-service/tests/e2e/defaults/main.test.bicep b/avm/res/search/search-service/tests/e2e/defaults/main.test.bicep index 46be064fce..dd0fc6e895 100644 --- a/avm/res/search/search-service/tests/e2e/defaults/main.test.bicep +++ b/avm/res/search/search-service/tests/e2e/defaults/main.test.bicep @@ -35,11 +35,14 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // Test Execution // // ============== // -module testDeployment '../../../main.bicep' = { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' - params: { - name: '${namePrefix}${serviceShort}002' - location: resourceLocation +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}002' + location: resourceLocation + } } -} +] diff --git a/avm/res/search/search-service/tests/e2e/max/main.test.bicep b/avm/res/search/search-service/tests/e2e/max/main.test.bicep index c53bbed9c1..0fc341ea23 100644 --- a/avm/res/search/search-service/tests/e2e/max/main.test.bicep +++ b/avm/res/search/search-service/tests/e2e/max/main.test.bicep @@ -58,86 +58,91 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // Test Execution // // ============== // -module testDeployment '../../../main.bicep' = { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - sku: 'standard3' - cmkEnforcement: 'Enabled' - disableLocalAuth: false - authOptions: { - aadOrApiKey: { - aadAuthFailureMode: 'http401WithBearerChallenge' - } - } - hostingMode: 'highDensity' - partitionCount: 2 - replicaCount: 3 - semanticSearch: 'standard' - managedIdentities: { - systemAssigned: true - userAssignedResourceIds: [ - nestedDependencies.outputs.managedIdentityResourceId - ] - } - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } - roleAssignments: [ - { - roleDefinitionIdOrName: 'Owner' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + sku: 'standard3' + cmkEnforcement: 'Enabled' + disableLocalAuth: false + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' + } } - { - roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' + hostingMode: 'highDensity' + partitionCount: 2 + replicaCount: 3 + semanticSearch: 'standard' + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] } - { - roleDefinitionIdOrName: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'acdd72a7-3385-48ef-bd42-f606fba81ae7' - ) - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' } - ] - networkRuleSet: { - ipRules: [ + roleAssignments: [ { - value: '40.74.28.0/23' + name: '73ec30e0-2e25-475f-beec-d90cab332eb7' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' } { - value: '87.147.204.13' + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' } ] - } - diagnosticSettings: [ - { - name: 'customSetting' - metricCategories: [ + networkRuleSet: { + ipRules: [ + { + value: '40.74.28.0/23' + } { - category: 'AllMetrics' + value: '87.147.204.13' } ] - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } - ] - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] -} +] diff --git a/avm/res/search/search-service/tests/e2e/pe/main.test.bicep b/avm/res/search/search-service/tests/e2e/pe/main.test.bicep index 1743c384cc..185cd85d5f 100644 --- a/avm/res/search/search-service/tests/e2e/pe/main.test.bicep +++ b/avm/res/search/search-service/tests/e2e/pe/main.test.bicep @@ -48,54 +48,65 @@ module nestedDependencies 'dependencies.bicep' = { // Test Execution // // ============== // -module testDeployment '../../../main.bicep' = { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - publicNetworkAccess: 'disabled' - privateEndpoints: [ - { - applicationSecurityGroupResourceIds: [ - nestedDependencies.outputs.applicationSecurityGroupResourceId - ] - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] - subnetResourceId: nestedDependencies.outputs.subnetResourceId - tags: { - Environment: 'Non-Prod' - Role: 'DeploymentValidation' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + publicNetworkAccess: 'Disabled' + privateEndpoints: [ + { + applicationSecurityGroupResourceIds: [ + nestedDependencies.outputs.applicationSecurityGroupResourceId + ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } } + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + sharedPrivateLinkResources: [ + { + privateLinkResourceId: nestedDependencies.outputs.storageAccountResourceId + groupId: 'blob' + resourceRegion: nestedDependencies.outputs.storageAccountLocation + requestMessage: 'Please approve this request' + } + { + privateLinkResourceId: nestedDependencies.outputs.keyVaultResourceId + groupId: 'vault' + requestMessage: 'Please approve this request' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' } - { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] - subnetResourceId: nestedDependencies.outputs.subnetResourceId - } - ] - sharedPrivateLinkResources: [ - { - privateLinkResourceId: nestedDependencies.outputs.storageAccountResourceId - groupId: 'blob' - resourceRegion: nestedDependencies.outputs.storageAccountLocation - requestMessage: 'Please approve this request' - } - { - privateLinkResourceId: nestedDependencies.outputs.keyVaultResourceId - groupId: 'vault' - requestMessage: 'Please approve this request' - } - ] - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' } + dependsOn: [ + nestedDependencies + ] } - dependsOn: [ - nestedDependencies - ] -} +] diff --git a/avm/res/search/search-service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/search/search-service/tests/e2e/waf-aligned/main.test.bicep index 343f301ded..1d58f7f339 100644 --- a/avm/res/search/search-service/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/search/search-service/tests/e2e/waf-aligned/main.test.bicep @@ -58,62 +58,65 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // Test Execution // // ============== // -module testDeployment '../../../main.bicep' = { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' - params: { - location: resourceLocation - name: '${namePrefix}${serviceShort}001' - sku: 'standard3' - cmkEnforcement: 'Enabled' - disableLocalAuth: false - authOptions: { - aadOrApiKey: { - aadAuthFailureMode: 'http401WithBearerChallenge' - } - } - hostingMode: 'highDensity' - partitionCount: 2 - replicaCount: 3 - managedIdentities: { - systemAssigned: true - } - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } - networkRuleSet: { - ipRules: [ - { - value: '40.74.28.0/23' - } - { - value: '87.147.204.13' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + sku: 'standard3' + cmkEnforcement: 'Enabled' + disableLocalAuth: false + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' } - ] - } - diagnosticSettings: [ - { - name: 'customSetting' - metricCategories: [ + } + hostingMode: 'highDensity' + partitionCount: 2 + replicaCount: 3 + managedIdentities: { + systemAssigned: true + } + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + networkRuleSet: { + ipRules: [ { - category: 'AllMetrics' + value: '40.74.28.0/23' + } + { + value: '87.147.204.13' } ] - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId } - ] - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] -} +] diff --git a/avm/res/search/search-service/version.json b/avm/res/search/search-service/version.json index 3f863a2bec..e42c3d9e5f 100644 --- a/avm/res/search/search-service/version.json +++ b/avm/res/search/search-service/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/service-bus/namespace/README.md b/avm/res/service-bus/namespace/README.md index 4ef8712ce2..91b4f6ae1b 100644 --- a/avm/res/service-bus/namespace/README.md +++ b/avm/res/service-bus/namespace/README.md @@ -18,8 +18,8 @@ This module deploys a Service Bus Namespace. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.ServiceBus/namespaces` | [2022-10-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2022-10-01-preview/namespaces) | | `Microsoft.ServiceBus/namespaces/AuthorizationRules` | [2022-10-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2022-10-01-preview/namespaces/AuthorizationRules) | | `Microsoft.ServiceBus/namespaces/disasterRecoveryConfigs` | [2022-10-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceBus/2022-10-01-preview/namespaces/disasterRecoveryConfigs) | @@ -295,9 +295,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } ] name: 'myPrivateEndpoint' - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } privateLinkServiceConnectionName: 'customLinkName' subnetResourceId: '' tags: { @@ -307,9 +311,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -340,16 +348,38 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { { principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] } ] roleAssignments: [ + { + name: '2c42f915-20bf-4094-ba42-fee1f811d374' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } { principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: '' } ] tags: { @@ -381,7 +411,17 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { { principalId: '' principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' } ] subscriptions: [ @@ -529,9 +569,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } ], "name": "myPrivateEndpoint", - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "privateLinkServiceConnectionName": "customLinkName", "subnetResourceId": "", "tags": { @@ -541,9 +585,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -578,7 +626,17 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { { "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] } @@ -587,9 +645,21 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "roleAssignments": { "value": [ { + "name": "2c42f915-20bf-4094-ba42-fee1f811d374", "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ] }, @@ -625,7 +695,17 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { { "principalId": "", "principalType": "ServicePrincipal", - "roleDefinitionIdOrName": "Reader" + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" } ], "subscriptions": [ @@ -727,9 +807,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { premiumMessagingPartitions: 1 privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'namespace' subnetResourceId: '' tags: { @@ -902,9 +986,13 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "namespace", "subnetResourceId": "", "tags": { @@ -1649,8 +1737,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -1834,19 +1921,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -1884,6 +2016,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1934,6 +2067,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2230,6 +2370,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-queuesroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-queuesroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-queuesroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-queuesroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-queuesroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `queues.roleAssignments.principalId` @@ -2280,6 +2421,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `queues.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `queues.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2348,6 +2496,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2398,6 +2547,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2624,6 +2780,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-topicsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-topicsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-topicsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-topicsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-topicsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `topics.roleAssignments.principalId` @@ -2674,6 +2831,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `topics.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `topics.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2919,6 +3083,7 @@ Enabling this property creates a Premium Service Bus Namespace in regions suppor | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the deployed service bus namespace. | +| `privateEndpoints` | array | The private endpoints of the service bus namespace. | | `resourceGroupName` | string | The resource group of the deployed service bus namespace. | | `resourceId` | string | The resource ID of the deployed service bus namespace. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | @@ -2929,7 +3094,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/service-bus/namespace/authorization-rule/main.json b/avm/res/service-bus/namespace/authorization-rule/main.json index 8a0020d80f..e78d5a84b0 100644 --- a/avm/res/service-bus/namespace/authorization-rule/main.json +++ b/avm/res/service-bus/namespace/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16894150656839211462" + "version": "0.29.47.4906", + "templateHash": "16659347344675880024" }, "name": "Service Bus Namespace Authorization Rules", "description": "This module deploys a Service Bus Namespace Authorization Rule.", diff --git a/avm/res/service-bus/namespace/disaster-recovery-config/main.json b/avm/res/service-bus/namespace/disaster-recovery-config/main.json index 9a59ce66a5..93ecd9ffa9 100644 --- a/avm/res/service-bus/namespace/disaster-recovery-config/main.json +++ b/avm/res/service-bus/namespace/disaster-recovery-config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5291933683239809881" + "version": "0.29.47.4906", + "templateHash": "2437567647402328568" }, "name": "Service Bus Namespace Disaster Recovery Configs", "description": "This module deploys a Service Bus Namespace Disaster Recovery Config", diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index a38fd0e5bc..507297992b 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -136,6 +136,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.servicebus-namespace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -354,7 +365,7 @@ resource serviceBusNamespace_diagnosticSettings 'Microsoft.Insights/diagnosticSe } ] -module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-serviceBusNamespace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -395,8 +406,7 @@ module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-e 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -408,14 +418,14 @@ module serviceBusNamespace_privateEndpoints 'br/public:avm/res/network/private-e ] resource serviceBusNamespace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(serviceBusNamespace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + serviceBusNamespace.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -442,6 +452,17 @@ output systemAssignedMIPrincipalId string = serviceBusNamespace.?identity.?princ @description('The location the resource was deployed into.') output location string = serviceBusNamespace.location +@description('The private endpoints of the service bus namespace.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: serviceBusNamespace_privateEndpoints[i].outputs.name + resourceId: serviceBusNamespace_privateEndpoints[i].outputs.resourceId + groupId: serviceBusNamespace_privateEndpoints[i].outputs.groupId + customDnsConfig: serviceBusNamespace_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: serviceBusNamespace_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -463,6 +484,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -501,11 +525,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index 0dc8f07057..e3bdecdc0f 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14555459005793836910" + "version": "0.29.47.4906", + "templateHash": "10964684048264975988" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -1272,6 +1302,13 @@ } }, "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'), 'userAssignedResourcesIds'), 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'), 'userAssignedResourcesIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -1419,20 +1456,20 @@ "serviceBusNamespace_roleAssignments": { "copy": { "name": "serviceBusNamespace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceBus/namespaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceBus/namespaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceBus/namespaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "serviceBusNamespace" @@ -1468,8 +1505,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16894150656839211462" + "version": "0.29.47.4906", + "templateHash": "16659347344675880024" }, "name": "Service Bus Namespace Authorization Rules", "description": "This module deploys a Service Bus Namespace Authorization Rule.", @@ -1572,8 +1609,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5291933683239809881" + "version": "0.29.47.4906", + "templateHash": "2437567647402328568" }, "name": "Service Bus Namespace Disaster Recovery Configs", "description": "This module deploys a Service Bus Namespace Disaster Recovery Config", @@ -1677,8 +1714,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1979996026271958468" + "version": "0.29.47.4906", + "templateHash": "5021121087195745079" }, "name": "Service Bus Namespace Migration Configuration", "description": "This module deploys a Service Bus Namespace Migration Configuration.", @@ -1782,8 +1819,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2567977628816180304" + "version": "0.29.47.4906", + "templateHash": "18220065019802173538" }, "name": "Service Bus Namespace Network Rule Sets", "description": "This module deploys a ServiceBus Namespace Network Rule Set.", @@ -1982,8 +2019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16865717026433728292" + "version": "0.29.47.4906", + "templateHash": "7247345844358420118" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -2020,6 +2057,13 @@ "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": { @@ -2243,6 +2287,13 @@ } }, "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": { "Azure Service Bus Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')]", "Azure Service Bus Data Receiver": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')]", @@ -2304,20 +2355,20 @@ "queue_roleAssignments": { "copy": { "name": "queue_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceBus/namespaces/{0}/queues/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceBus/namespaces/queues', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceBus/namespaces/queues', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2356,8 +2407,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15391519662555721522" + "version": "0.29.47.4906", + "templateHash": "13378473188831787359" }, "name": "Service Bus Namespace Queue Authorization Rules", "description": "This module deploys a Service Bus Namespace Queue Authorization Rule.", @@ -2537,8 +2588,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10953229984809736827" + "version": "0.29.47.4906", + "templateHash": "3425340822225922607" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -2575,6 +2626,13 @@ "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": { @@ -2926,6 +2984,13 @@ } }, "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": { "Azure Service Bus Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')]", "Azure Service Bus Data Receiver": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')]", @@ -2970,20 +3035,20 @@ "topic_roleAssignments": { "copy": { "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceBus/namespaces/{0}/topics/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceBus/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceBus/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topic" @@ -3020,8 +3085,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11438474699882792252" + "version": "0.29.47.4906", + "templateHash": "6843568331497160185" }, "name": "Service Bus Namespace Topic Authorization Rules", "description": "This module deploys a Service Bus Namespace Topic Authorization Rule.", @@ -3170,8 +3235,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13876094385420679023" + "version": "0.29.47.4906", + "templateHash": "13559505347925945415" }, "name": "Service Bus Namespace Topic Subscription", "description": "This module deploys a Service Bus Namespace Topic Subscription.", @@ -3421,11 +3486,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -3453,19 +3515,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -3569,13 +3659,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -3609,8 +3699,11 @@ "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." + "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": { @@ -3650,8 +3743,11 @@ "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." + "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": { @@ -3699,6 +3795,29 @@ } }, "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": { @@ -3734,18 +3853,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -3801,6 +3913,13 @@ } }, "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')]", @@ -3818,8 +3937,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3837,7 +3956,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3878,27 +3997,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -3909,28 +4028,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -3938,12 +4081,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -3957,27 +4103,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -4035,14 +4190,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -4087,6 +4256,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('serviceBusNamespace', '2022-10-01-preview', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the service bus namespace." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('serviceBusNamespace_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/service-bus/namespace/migration-configuration/main.json b/avm/res/service-bus/namespace/migration-configuration/main.json index 4b7f623c4b..47cb3b3ead 100644 --- a/avm/res/service-bus/namespace/migration-configuration/main.json +++ b/avm/res/service-bus/namespace/migration-configuration/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1979996026271958468" + "version": "0.29.47.4906", + "templateHash": "5021121087195745079" }, "name": "Service Bus Namespace Migration Configuration", "description": "This module deploys a Service Bus Namespace Migration Configuration.", diff --git a/avm/res/service-bus/namespace/network-rule-set/main.json b/avm/res/service-bus/namespace/network-rule-set/main.json index 58c40bbf6f..5f9ac47d13 100644 --- a/avm/res/service-bus/namespace/network-rule-set/main.json +++ b/avm/res/service-bus/namespace/network-rule-set/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2567977628816180304" + "version": "0.29.47.4906", + "templateHash": "18220065019802173538" }, "name": "Service Bus Namespace Network Rule Sets", "description": "This module deploys a ServiceBus Namespace Network Rule Set.", diff --git a/avm/res/service-bus/namespace/queue/README.md b/avm/res/service-bus/namespace/queue/README.md index cf3db6cbdf..91dea8e3c9 100644 --- a/avm/res/service-bus/namespace/queue/README.md +++ b/avm/res/service-bus/namespace/queue/README.md @@ -254,6 +254,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -304,6 +305,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/service-bus/namespace/queue/authorization-rule/main.json b/avm/res/service-bus/namespace/queue/authorization-rule/main.json index 978ee618a5..2ac613b3a0 100644 --- a/avm/res/service-bus/namespace/queue/authorization-rule/main.json +++ b/avm/res/service-bus/namespace/queue/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15391519662555721522" + "version": "0.29.47.4906", + "templateHash": "13378473188831787359" }, "name": "Service Bus Namespace Queue Authorization Rules", "description": "This module deploys a Service Bus Namespace Queue Authorization Rule.", diff --git a/avm/res/service-bus/namespace/queue/main.bicep b/avm/res/service-bus/namespace/queue/main.bicep index 3f406d0cbb..c8375f00a1 100644 --- a/avm/res/service-bus/namespace/queue/main.bicep +++ b/avm/res/service-bus/namespace/queue/main.bicep @@ -106,6 +106,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource namespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' existing = { name: namespaceName } @@ -157,14 +168,10 @@ resource queue_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock } resource queue_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(queue.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(queue.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -198,6 +205,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/service-bus/namespace/queue/main.json b/avm/res/service-bus/namespace/queue/main.json index d7f75e9c96..7eb7008009 100644 --- a/avm/res/service-bus/namespace/queue/main.json +++ b/avm/res/service-bus/namespace/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16865717026433728292" + "version": "0.29.47.4906", + "templateHash": "7247345844358420118" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -43,6 +43,13 @@ "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": { @@ -266,6 +273,13 @@ } }, "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": { "Azure Service Bus Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')]", "Azure Service Bus Data Receiver": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')]", @@ -327,20 +341,20 @@ "queue_roleAssignments": { "copy": { "name": "queue_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceBus/namespaces/{0}/queues/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceBus/namespaces/queues', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceBus/namespaces/queues', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -379,8 +393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15391519662555721522" + "version": "0.29.47.4906", + "templateHash": "13378473188831787359" }, "name": "Service Bus Namespace Queue Authorization Rules", "description": "This module deploys a Service Bus Namespace Queue Authorization Rule.", diff --git a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep index b08e017836..75f7aac495 100644 --- a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep @@ -84,9 +84,23 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + name: '2c42f915-20bf-4094-ba42-fee1f811d374' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' } ] @@ -132,7 +146,20 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}q001' roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -163,7 +190,20 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}t001' roleAssignments: [ { - roleDefinitionIdOrName: 'Reader' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } @@ -216,9 +256,13 @@ module testDeployment '../../../main.bicep' = [ name: 'myPrivateEndpoint' privateLinkServiceConnectionName: 'customLinkName' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' @@ -245,9 +289,13 @@ module testDeployment '../../../main.bicep' = [ } { subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } } ] managedIdentities: { diff --git a/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep index cbd896930d..f40c29ea50 100644 --- a/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -180,9 +180,13 @@ module testDeployment '../../../main.bicep' = [ { service: 'namespace' subnetResourceId: nestedDependencies.outputs.subnetResourceId - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/service-bus/namespace/topic/README.md b/avm/res/service-bus/namespace/topic/README.md index 3da5c7b960..19dfd2ed56 100644 --- a/avm/res/service-bus/namespace/topic/README.md +++ b/avm/res/service-bus/namespace/topic/README.md @@ -219,6 +219,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -269,6 +270,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/service-bus/namespace/topic/authorization-rule/main.json b/avm/res/service-bus/namespace/topic/authorization-rule/main.json index cbe91a6bb8..60d8ebef9f 100644 --- a/avm/res/service-bus/namespace/topic/authorization-rule/main.json +++ b/avm/res/service-bus/namespace/topic/authorization-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11438474699882792252" + "version": "0.29.47.4906", + "templateHash": "6843568331497160185" }, "name": "Service Bus Namespace Topic Authorization Rules", "description": "This module deploys a Service Bus Namespace Topic Authorization Rule.", diff --git a/avm/res/service-bus/namespace/topic/main.bicep b/avm/res/service-bus/namespace/topic/main.bicep index d59e2a85b1..9b4683fb7c 100644 --- a/avm/res/service-bus/namespace/topic/main.bicep +++ b/avm/res/service-bus/namespace/topic/main.bicep @@ -105,6 +105,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource namespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' existing = { name: namespaceName } @@ -157,14 +168,10 @@ resource topic_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock } resource topic_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(topic.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -223,6 +230,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/service-bus/namespace/topic/main.json b/avm/res/service-bus/namespace/topic/main.json index 30c735aa77..55bcfc9651 100644 --- a/avm/res/service-bus/namespace/topic/main.json +++ b/avm/res/service-bus/namespace/topic/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10953229984809736827" + "version": "0.29.47.4906", + "templateHash": "3425340822225922607" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -43,6 +43,13 @@ "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": { @@ -394,6 +401,13 @@ } }, "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": { "Azure Service Bus Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')]", "Azure Service Bus Data Receiver": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')]", @@ -438,20 +452,20 @@ "topic_roleAssignments": { "copy": { "name": "topic_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceBus/namespaces/{0}/topics/{1}', parameters('namespaceName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceBus/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceBus/namespaces/topics', parameters('namespaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "topic" @@ -488,8 +502,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11438474699882792252" + "version": "0.29.47.4906", + "templateHash": "6843568331497160185" }, "name": "Service Bus Namespace Topic Authorization Rules", "description": "This module deploys a Service Bus Namespace Topic Authorization Rule.", @@ -638,8 +652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13876094385420679023" + "version": "0.29.47.4906", + "templateHash": "13559505347925945415" }, "name": "Service Bus Namespace Topic Subscription", "description": "This module deploys a Service Bus Namespace Topic Subscription.", diff --git a/avm/res/service-bus/namespace/topic/subscription/main.json b/avm/res/service-bus/namespace/topic/subscription/main.json index c9949f08b8..251f6038f1 100644 --- a/avm/res/service-bus/namespace/topic/subscription/main.json +++ b/avm/res/service-bus/namespace/topic/subscription/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13876094385420679023" + "version": "0.29.47.4906", + "templateHash": "13559505347925945415" }, "name": "Service Bus Namespace Topic Subscription", "description": "This module deploys a Service Bus Namespace Topic Subscription.", diff --git a/avm/res/service-bus/namespace/version.json b/avm/res/service-bus/namespace/version.json index 41fc8c654f..09c3664cec 100644 --- a/avm/res/service-bus/namespace/version.json +++ b/avm/res/service-bus/namespace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.7", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/service-fabric/cluster/README.md b/avm/res/service-fabric/cluster/README.md index 759b3d695f..702b9d7c6c 100644 --- a/avm/res/service-fabric/cluster/README.md +++ b/avm/res/service-fabric/cluster/README.md @@ -275,11 +275,13 @@ module cluster 'br/public:avm/res/service-fabric/cluster:' = { ] roleAssignments: [ { + name: '26b52f01-eebc-4056-a516-41541369258c' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -491,11 +493,13 @@ module cluster 'br/public:avm/res/service-fabric/cluster:' = { "roleAssignments": { "value": [ { + "name": "26b52f01-eebc-4056-a516-41541369258c", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1353,6 +1357,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1403,6 +1408,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/service-fabric/cluster/application-type/main.json b/avm/res/service-fabric/cluster/application-type/main.json index 92a20e2771..b9a413bced 100644 --- a/avm/res/service-fabric/cluster/application-type/main.json +++ b/avm/res/service-fabric/cluster/application-type/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10847125060034010702" + "version": "0.29.47.4906", + "templateHash": "8632559494589711330" }, "name": "Service Fabric Cluster Application Types", "description": "This module deploys a Service Fabric Cluster Application Type.", diff --git a/avm/res/service-fabric/cluster/main.bicep b/avm/res/service-fabric/cluster/main.bicep index fa5fb4fe72..cef122c80f 100644 --- a/avm/res/service-fabric/cluster/main.bicep +++ b/avm/res/service-fabric/cluster/main.bicep @@ -256,6 +256,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.servicefabric-cluster.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -388,14 +399,14 @@ resource serviceFabricCluster_lock 'Microsoft.Authorization/locks@2020-05-01' = // Service Fabric cluster RBAC assignment resource serviceFabricCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(serviceFabricCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + serviceFabricCluster.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -509,6 +520,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/service-fabric/cluster/main.json b/avm/res/service-fabric/cluster/main.json index 384cc1c4b4..ae343dc117 100644 --- a/avm/res/service-fabric/cluster/main.json +++ b/avm/res/service-fabric/cluster/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3456931451481989704" + "version": "0.29.47.4906", + "templateHash": "10632901635628178846" }, "name": "Service Fabric Clusters", "description": "This module deploys a Service Fabric Cluster.", @@ -176,6 +176,13 @@ "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": { @@ -553,6 +560,11 @@ "notificationLevel": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationLevel'), parameters('notifications')[copyIndex('notificationsVar')].notificationLevel, 'All')]", "notificationTargets": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationTargets'), parameters('notifications')[copyIndex('notificationsVar')].notificationTargets, createArray())]" } + }, + { + "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)))))]" } ], "upgradeDescriptionVar": "[union(createObject('deltaHealthPolicy', createObject('applicationDeltaHealthPolicies', coalesce(tryGet(parameters('upgradeDescription'), 'applicationDeltaHealthPolicies'), createObject()), 'maxPercentDeltaUnhealthyApplications', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyApplications'), 0), 'maxPercentDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyNodes'), 0), 'maxPercentUpgradeDomainDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentUpgradeDomainDeltaUnhealthyNodes'), 0)), 'forceRestart', if(contains(parameters('upgradeDescription'), 'forceRestart'), parameters('upgradeDescription').forceRestart, false()), 'healthCheckRetryTimeout', if(contains(parameters('upgradeDescription'), 'healthCheckRetryTimeout'), parameters('upgradeDescription').healthCheckRetryTimeout, '00:45:00'), 'healthCheckStableDuration', if(contains(parameters('upgradeDescription'), 'healthCheckStableDuration'), parameters('upgradeDescription').healthCheckStableDuration, '00:01:00'), 'healthCheckWaitDuration', if(contains(parameters('upgradeDescription'), 'healthCheckWaitDuration'), parameters('upgradeDescription').healthCheckWaitDuration, '00:00:30'), 'upgradeDomainTimeout', if(contains(parameters('upgradeDescription'), 'upgradeDomainTimeout'), parameters('upgradeDescription').upgradeDomainTimeout, '02:00:00'), 'upgradeReplicaSetCheckTimeout', if(contains(parameters('upgradeDescription'), 'upgradeReplicaSetCheckTimeout'), parameters('upgradeDescription').upgradeReplicaSetCheckTimeout, '1.00:00:00'), 'upgradeTimeout', if(contains(parameters('upgradeDescription'), 'upgradeTimeout'), parameters('upgradeDescription').upgradeTimeout, '02:00:00')), if(contains(parameters('upgradeDescription'), 'healthPolicy'), createObject('healthPolicy', createObject('applicationHealthPolicies', if(contains(parameters('upgradeDescription').healthPolicy, 'applicationHealthPolicies'), parameters('upgradeDescription').healthPolicy.applicationHealthPolicies, createObject()), 'maxPercentUnhealthyApplications', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyApplications'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyApplications, 0), 'maxPercentUnhealthyNodes', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyNodes'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyNodes, 0))), createObject()))]", @@ -640,20 +652,20 @@ "serviceFabricCluster_roleAssignments": { "copy": { "name": "serviceFabricCluster_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.ServiceFabric/clusters/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.ServiceFabric/clusters', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceFabric/clusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "serviceFabricCluster" @@ -690,8 +702,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10847125060034010702" + "version": "0.29.47.4906", + "templateHash": "8632559494589711330" }, "name": "Service Fabric Cluster Application Types", "description": "This module deploys a Service Fabric Cluster Application Type.", diff --git a/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep index 4a496447d9..ef99d5fbbe 100644 --- a/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep +++ b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep @@ -200,11 +200,13 @@ module testDeployment '../../../main.bicep' = [ vmImage: 'Linux' roleAssignments: [ { + name: '26b52f01-eebc-4056-a516-41541369258c' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/service-fabric/cluster/version.json b/avm/res/service-fabric/cluster/version.json index daf1a794d9..76049e1c4a 100644 --- a/avm/res/service-fabric/cluster/version.json +++ b/avm/res/service-fabric/cluster/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/signal-r-service/signal-r/README.md b/avm/res/signal-r-service/signal-r/README.md index 9466ec00a2..d182a6aca4 100644 --- a/avm/res/signal-r-service/signal-r/README.md +++ b/avm/res/signal-r-service/signal-r/README.md @@ -22,8 +22,8 @@ This module deploys a SignalR Service SignalR. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.SignalRService/signalR` | [2022-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.SignalRService/2022-02-01/signalR) | ## Usage examples @@ -134,9 +134,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -145,9 +149,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -156,11 +164,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { ] roleAssignments: [ { + name: 'd8c98876-5377-4b49-98ae-41a8b5537761' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -247,9 +257,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -258,9 +272,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -273,11 +291,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { "roleAssignments": { "value": [ { + "name": "d8c98876-5377-4b49-98ae-41a8b5537761", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -350,9 +370,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -434,9 +458,13 @@ module signalR 'br/public:avm/res/signal-r-service/signal-r:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -693,8 +721,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -878,19 +905,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -928,6 +1000,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -978,6 +1051,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1066,6 +1146,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1116,6 +1197,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1190,6 +1278,7 @@ Upstream templates to enable. For more information, see https://learn.microsoft. | :-- | :-- | :-- | | `location` | string | The location the resource was deployed into. | | `name` | string | The SignalR name. | +| `privateEndpoints` | array | The private endpoints of the SignalR. | | `resourceGroupName` | string | The SignalR resource group. | | `resourceId` | string | The SignalR resource ID. | @@ -1199,7 +1288,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/signal-r-service/signal-r/main.bicep b/avm/res/signal-r-service/signal-r/main.bicep index 5bab5fb4ea..37b7449350 100644 --- a/avm/res/signal-r-service/signal-r/main.bicep +++ b/avm/res/signal-r-service/signal-r/main.bicep @@ -170,6 +170,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.signalrservice-signalr.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -229,7 +240,7 @@ resource signalR 'Microsoft.SignalRService/signalR@2022-02-01' = { } } -module signalR_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module signalR_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-signalR-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -270,8 +281,7 @@ module signalR_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -294,14 +304,10 @@ resource signalR_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lo } resource signalR_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(signalR.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(signalR.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -325,6 +331,17 @@ output resourceId string = signalR.id @description('The location the resource was deployed into.') output location string = signalR.location +@description('The private endpoints of the SignalR.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: signalR_privateEndpoints[i].outputs.name + resourceId: signalR_privateEndpoints[i].outputs.resourceId + groupId: signalR_privateEndpoints[i].outputs.groupId + customDnsConfig: signalR_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: signalR_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -338,6 +355,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -376,11 +396,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/signal-r-service/signal-r/main.json b/avm/res/signal-r-service/signal-r/main.json index b85ec6c88c..6ed88a6fad 100644 --- a/avm/res/signal-r-service/signal-r/main.json +++ b/avm/res/signal-r-service/signal-r/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13051033571565929361" + "version": "0.29.47.4906", + "templateHash": "6232258125767604001" }, "name": "SignalR Service SignalR", "description": "This module deploys a SignalR Service SignalR.", @@ -43,6 +43,13 @@ "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": { @@ -143,21 +150,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -507,6 +537,11 @@ "name": "[parameters('resourceLogConfigurationsToEnable')[copyIndex('resourceLogConfiguration')]]", "enabled": "true" } + }, + { + "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": { @@ -594,20 +629,20 @@ "signalR_roleAssignments": { "copy": { "name": "signalR_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.SignalRService/signalR/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.SignalRService/signalR', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.SignalRService/signalR', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "signalR" @@ -645,11 +680,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -677,19 +709,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -793,13 +853,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -833,8 +893,11 @@ "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." + "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": { @@ -874,8 +937,11 @@ "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." + "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": { @@ -923,6 +989,29 @@ } }, "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": { @@ -958,18 +1047,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -1025,6 +1107,13 @@ } }, "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')]", @@ -1042,8 +1131,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1061,7 +1150,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1102,27 +1191,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1133,28 +1222,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1162,12 +1275,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1181,27 +1297,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1259,14 +1384,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1304,6 +1443,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('signalR', '2022-02-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the SignalR." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('signalR_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('signalR_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('signalR_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('signalR_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('signalR_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/signal-r-service/signal-r/tests/e2e/max/main.test.bicep b/avm/res/signal-r-service/signal-r/tests/e2e/max/main.test.bicep index 21e8ab2c5b..8ec75e7dac 100644 --- a/avm/res/signal-r-service/signal-r/tests/e2e/max/main.test.bicep +++ b/avm/res/signal-r-service/signal-r/tests/e2e/max/main.test.bicep @@ -84,9 +84,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' @@ -95,9 +99,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] @@ -106,11 +114,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'd8c98876-5377-4b49-98ae-41a8b5537761' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep b/avm/res/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep index 51ca8dd306..009b496f45 100644 --- a/avm/res/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/signal-r-service/signal-r/tests/e2e/waf-aligned/main.test.bicep @@ -79,9 +79,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { 'hidden-title': 'This is visible in the resource name' diff --git a/avm/res/signal-r-service/signal-r/version.json b/avm/res/signal-r-service/signal-r/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/signal-r-service/signal-r/version.json +++ b/avm/res/signal-r-service/signal-r/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/signal-r-service/web-pub-sub/README.md b/avm/res/signal-r-service/web-pub-sub/README.md index bd364eb46f..224f8310e8 100644 --- a/avm/res/signal-r-service/web-pub-sub/README.md +++ b/avm/res/signal-r-service/web-pub-sub/README.md @@ -22,8 +22,8 @@ This module deploys a SignalR Web PubSub Service. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.SignalRService/webPubSub` | [2021-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.SignalRService/2021-10-01/webPubSub) | ## Usage examples @@ -136,9 +136,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'webpubsub' subnetResourceId: '' tags: { @@ -148,9 +152,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { } } { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } subnetResourceId: '' } ] @@ -159,11 +167,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { ] roleAssignments: [ { + name: '8e40bf2f-0457-4292-a83a-eedc36d04f6a' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -252,9 +262,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "webpubsub", "subnetResourceId": "", "tags": { @@ -264,9 +278,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { } }, { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "subnetResourceId": "" } ] @@ -279,11 +297,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { "roleAssignments": { "value": [ { + "name": "8e40bf2f-0457-4292-a83a-eedc36d04f6a", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -358,9 +378,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - '' - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } service: 'webpubsub' subnetResourceId: '' tags: { @@ -445,9 +469,13 @@ module webPubSub 'br/public:avm/res/signal-r-service/web-pub-sub:' = { "privateEndpoints": { "value": [ { - "privateDnsZoneResourceIds": [ - "" - ], + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, "service": "webpubsub", "subnetResourceId": "", "tags": { @@ -662,8 +690,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | | [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | -| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | -| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | | [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | @@ -847,19 +874,64 @@ The name of the private endpoint. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneGroupName` +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` -The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. +The name of the private DNS zone group config. - Required: No - Type: string -### Parameter: `privateEndpoints.privateDnsZoneResourceIds` +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` -The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. +The name of the Private DNS Zone Group. - Required: No -- Type: array +- Type: string ### Parameter: `privateEndpoints.privateLinkServiceConnectionName` @@ -897,6 +969,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -947,6 +1020,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1035,6 +1115,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1085,6 +1166,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1133,6 +1221,7 @@ Tags of the resource. | `hostName` | string | The Web PubSub hostName. | | `location` | string | The location the resource was deployed into. | | `name` | string | The Web PubSub name. | +| `privateEndpoints` | array | The private endpoints of the Web PubSub. | | `publicPort` | int | The Web PubSub publicPort. | | `resourceGroupName` | string | The Web PubSub resource group. | | `resourceId` | string | The Web PubSub resource ID. | @@ -1145,7 +1234,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | ## Data Collection diff --git a/avm/res/signal-r-service/web-pub-sub/main.bicep b/avm/res/signal-r-service/web-pub-sub/main.bicep index 43a0cc5bbc..a3821042f8 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.bicep +++ b/avm/res/signal-r-service/web-pub-sub/main.bicep @@ -133,6 +133,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.signalrservice-webpubsub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -178,7 +189,7 @@ resource webPubSub 'Microsoft.SignalRService/webPubSub@2021-10-01' = { } } -module webPubSub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module webPubSub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-webPubSub-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -219,8 +230,7 @@ module webPubSub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0. 'Full' ).location lock: privateEndpoint.?lock ?? lock - privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName - privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup roleAssignments: privateEndpoint.?roleAssignments tags: privateEndpoint.?tags ?? tags customDnsConfigs: privateEndpoint.?customDnsConfigs @@ -243,14 +253,10 @@ resource webPubSub_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty( } resource webPubSub_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(webPubSub.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(webPubSub.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -289,6 +295,17 @@ output systemAssignedMIPrincipalId string = webPubSub.?identity.?principalId ?? @description('The location the resource was deployed into.') output location string = webPubSub.location +@description('The private endpoints of the Web PubSub.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: webPubSub_privateEndpoints[i].outputs.name + resourceId: webPubSub_privateEndpoints[i].outputs.resourceId + groupId: webPubSub_privateEndpoints[i].outputs.groupId + customDnsConfig: webPubSub_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: webPubSub_privateEndpoints[i].outputs.networkInterfaceIds + } +] + // =============== // // Definitions // // =============== // @@ -310,6 +327,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -348,11 +368,20 @@ type privateEndpointType = { @description('Required. Resource ID of the subnet where the endpoint needs to be created.') subnetResourceId: string - @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') - privateDnsZoneGroupName: string? + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? - @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') - privateDnsZoneResourceIds: string[]? + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? @description('Optional. If Manual Private Link Connection is required.') isManualConnection: bool? diff --git a/avm/res/signal-r-service/web-pub-sub/main.json b/avm/res/signal-r-service/web-pub-sub/main.json index 185a835fb4..5807cd4da1 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.json +++ b/avm/res/signal-r-service/web-pub-sub/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16916371778402298649" + "version": "0.29.47.4906", + "templateHash": "3619836062547426326" }, "name": "SignalR Web PubSub Services", "description": "This module deploys a SignalR Web PubSub Service.", @@ -66,6 +66,13 @@ "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": { @@ -166,21 +173,44 @@ "description": "Required. Resource ID of the subnet where the endpoint needs to be created." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", - "items": { - "type": "string" + "privateDnsZoneGroup": { + "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." + } + } }, "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "isManualConnection": { @@ -458,6 +488,11 @@ "name": "[parameters('resourceLogConfigurationsToEnable')[copyIndex('resourceLogConfiguration')]]", "enabled": "true" } + }, + { + "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'))))]", @@ -541,20 +576,20 @@ "webPubSub_roleAssignments": { "copy": { "name": "webPubSub_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.SignalRService/webPubSub/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.SignalRService/webPubSub', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.SignalRService/webPubSub', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "webPubSub" @@ -592,11 +627,8 @@ "lock": { "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" }, - "privateDnsZoneGroupName": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" - }, - "privateDnsZoneResourceIds": { - "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" @@ -624,19 +656,47 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", "owner": "Azure/module-maintainers" }, "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, "roleAssignmentType": { "type": "array", "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -740,13 +800,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -780,8 +840,11 @@ "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." + "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": { @@ -821,8 +884,11 @@ "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." + "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": { @@ -870,6 +936,29 @@ } }, "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": { @@ -905,18 +994,11 @@ "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." } }, - "privateDnsZoneGroupName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." - } - }, - "privateDnsZoneResourceIds": { - "type": "array", + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, "metadata": { - "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + "description": "Optional. The private DNS zone group to configure for the private endpoint." } }, "location": { @@ -972,6 +1054,13 @@ } }, "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')]", @@ -989,8 +1078,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1008,7 +1097,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1049,27 +1138,27 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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('privateDnsZoneResourceIds')))]", + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", @@ -1080,28 +1169,52 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" - }, - "privateDNSResourceIds": { - "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + "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.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", "owner": "Azure/module-maintainers" }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, "parameters": { "privateEndpointName": { "type": "string", @@ -1109,12 +1222,15 @@ "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." } }, - "privateDNSResourceIds": { + "privateDnsZoneConfigs": { "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, "minLength": 1, "maxLength": 5, "metadata": { - "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + "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": { @@ -1128,27 +1244,36 @@ "variables": { "copy": [ { - "name": "privateDnsZoneConfigs", - "count": "[length(parameters('privateDNSResourceIds'))]", + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", "input": { - "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", "properties": { - "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" } } } ] }, - "resources": [ - { + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { - "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" - } + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] } - ], + }, "outputs": { "name": { "type": "string", @@ -1206,14 +1331,28 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "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(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1286,6 +1425,22 @@ "description": "The location the resource was deployed into." }, "value": "[reference('webPubSub', '2021-10-01', 'full').location]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the Web PubSub." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('webPubSub_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('webPubSub_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('webPubSub_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('webPubSub_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('webPubSub_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } } } } \ No newline at end of file diff --git a/avm/res/signal-r-service/web-pub-sub/tests/e2e/max/main.test.bicep b/avm/res/signal-r-service/web-pub-sub/tests/e2e/max/main.test.bicep index 9d4e07833f..61f7b3dc9d 100644 --- a/avm/res/signal-r-service/web-pub-sub/tests/e2e/max/main.test.bicep +++ b/avm/res/signal-r-service/web-pub-sub/tests/e2e/max/main.test.bicep @@ -83,9 +83,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'webpubsub' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { @@ -95,9 +99,13 @@ module testDeployment '../../../main.bicep' = [ } } { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] @@ -106,11 +114,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '8e40bf2f-0457-4292-a83a-eedc36d04f6a' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep b/avm/res/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep index a1ba316a76..34172c1749 100644 --- a/avm/res/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/signal-r-service/web-pub-sub/tests/e2e/waf-aligned/main.test.bicep @@ -78,9 +78,13 @@ module testDeployment '../../../main.bicep' = [ } privateEndpoints: [ { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } service: 'webpubsub' subnetResourceId: nestedDependencies.outputs.subnetResourceId tags: { diff --git a/avm/res/signal-r-service/web-pub-sub/version.json b/avm/res/signal-r-service/web-pub-sub/version.json index 1c035df49f..3f863a2bec 100644 --- a/avm/res/signal-r-service/web-pub-sub/version.json +++ b/avm/res/signal-r-service/web-pub-sub/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/sql/server/README.md b/avm/res/sql/server/README.md index acc13b5f5b..7a171c098a 100644 --- a/avm/res/sql/server/README.md +++ b/avm/res/sql/server/README.md @@ -19,8 +19,8 @@ This module deploys an Azure SQL Server. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Sql/servers` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/servers) | | `Microsoft.Sql/servers/auditingSettings` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/servers/auditingSettings) | | `Microsoft.Sql/servers/databases` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/servers/databases) | @@ -350,11 +350,13 @@ module server 'br/public:avm/res/sql/server:' = { restrictOutboundNetworkAccess: 'Disabled' roleAssignments: [ { + name: '7027a5c5-d1b1-49e0-80cc-ffdff3a3ada9' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -528,11 +530,13 @@ module server 'br/public:avm/res/sql/server:' = { "roleAssignments": { "value": [ { + "name": "7027a5c5-d1b1-49e0-80cc-ffdff3a3ada9", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1667,6 +1671,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1717,6 +1722,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1802,6 +1814,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1852,6 +1865,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1917,7 +1937,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Notes diff --git a/avm/res/sql/server/audit-settings/main.json b/avm/res/sql/server/audit-settings/main.json index 44c291e03d..e18c9a0494 100644 --- a/avm/res/sql/server/audit-settings/main.json +++ b/avm/res/sql/server/audit-settings/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7842020933584780357" + "version": "0.29.47.4906", + "templateHash": "4165841300638093382" }, "name": "Azure SQL Server Audit Settings", "description": "This module deploys an Azure SQL Server Audit Settings.", @@ -144,8 +144,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13426110187156530306" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { diff --git a/avm/res/sql/server/database/README.md b/avm/res/sql/server/database/README.md index 48abd9e22f..304baed8c1 100644 --- a/avm/res/sql/server/database/README.md +++ b/avm/res/sql/server/database/README.md @@ -484,7 +484,7 @@ Whether or not this database is zone redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/sql/server/database/backup-long-term-retention-policy/main.json b/avm/res/sql/server/database/backup-long-term-retention-policy/main.json index 77126b341b..b54683c3bc 100644 --- a/avm/res/sql/server/database/backup-long-term-retention-policy/main.json +++ b/avm/res/sql/server/database/backup-long-term-retention-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10544886139268607584" + "version": "0.29.47.4906", + "templateHash": "2778016138108001251" }, "name": "SQL Server Database Long Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy.", diff --git a/avm/res/sql/server/database/backup-short-term-retention-policy/main.json b/avm/res/sql/server/database/backup-short-term-retention-policy/main.json index 320c2278dc..842e54ba64 100644 --- a/avm/res/sql/server/database/backup-short-term-retention-policy/main.json +++ b/avm/res/sql/server/database/backup-short-term-retention-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2539756678143412969" + "version": "0.29.47.4906", + "templateHash": "8635162595153731245" }, "name": "Azure SQL Server Database Short Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy.", diff --git a/avm/res/sql/server/database/main.bicep b/avm/res/sql/server/database/main.bicep index ff985b8eb6..f26259ab84 100644 --- a/avm/res/sql/server/database/main.bicep +++ b/avm/res/sql/server/database/main.bicep @@ -41,7 +41,7 @@ param maxSizeBytes int = 34359738368 param sampleName string = '' @description('Optional. Whether or not this database is zone redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. The license type to apply for this database.') param licenseType string = '' diff --git a/avm/res/sql/server/database/main.json b/avm/res/sql/server/database/main.json index e0fb549908..a28b55efe8 100644 --- a/avm/res/sql/server/database/main.json +++ b/avm/res/sql/server/database/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9732417431098147370" + "version": "0.29.47.4906", + "templateHash": "6019999815954957727" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -217,7 +217,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this database is zone redundant." } @@ -483,8 +483,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2539756678143412969" + "version": "0.29.47.4906", + "templateHash": "8635162595153731245" }, "name": "Azure SQL Server Database Short Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy.", @@ -585,8 +585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10544886139268607584" + "version": "0.29.47.4906", + "templateHash": "2778016138108001251" }, "name": "SQL Server Database Long Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy.", diff --git a/avm/res/sql/server/elastic-pool/README.md b/avm/res/sql/server/elastic-pool/README.md index fb55fe4ad4..d787a2e320 100644 --- a/avm/res/sql/server/elastic-pool/README.md +++ b/avm/res/sql/server/elastic-pool/README.md @@ -168,7 +168,7 @@ Whether or not this elastic pool is zone redundant, which means the replicas of - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/sql/server/elastic-pool/main.bicep b/avm/res/sql/server/elastic-pool/main.bicep index 2e884fbc49..606aded8d8 100644 --- a/avm/res/sql/server/elastic-pool/main.bicep +++ b/avm/res/sql/server/elastic-pool/main.bicep @@ -49,7 +49,7 @@ param databaseMaxCapacity int = 2 param databaseMinCapacity int = 0 @description('Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones.') -param zoneRedundant bool = false +param zoneRedundant bool = true resource server 'Microsoft.Sql/servers@2023-08-01-preview' existing = { name: serverName diff --git a/avm/res/sql/server/elastic-pool/main.json b/avm/res/sql/server/elastic-pool/main.json index 90d43cc984..018727aa52 100644 --- a/avm/res/sql/server/elastic-pool/main.json +++ b/avm/res/sql/server/elastic-pool/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9517065595416154313" + "version": "0.29.47.4906", + "templateHash": "18037703368269722870" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -115,7 +115,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." } diff --git a/avm/res/sql/server/encryption-protector/main.json b/avm/res/sql/server/encryption-protector/main.json index dcfd337c06..8e8deb599e 100644 --- a/avm/res/sql/server/encryption-protector/main.json +++ b/avm/res/sql/server/encryption-protector/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2864913755091701450" + "version": "0.29.47.4906", + "templateHash": "6914924378490463775" }, "name": "Azure SQL Server Encryption Protector", "description": "This module deploys an Azure SQL Server Encryption Protector.", diff --git a/avm/res/sql/server/firewall-rule/main.json b/avm/res/sql/server/firewall-rule/main.json index e848ad32b2..006ddcd226 100644 --- a/avm/res/sql/server/firewall-rule/main.json +++ b/avm/res/sql/server/firewall-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5065747305708606737" + "version": "0.29.47.4906", + "templateHash": "7779473510493338097" }, "name": "Azure SQL Server Firewall Rule", "description": "This module deploys an Azure SQL Server Firewall Rule.", diff --git a/avm/res/sql/server/key/main.json b/avm/res/sql/server/key/main.json index a2f6b6ba90..78e43db8f4 100644 --- a/avm/res/sql/server/key/main.json +++ b/avm/res/sql/server/key/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15985945273078840265" + "version": "0.29.47.4906", + "templateHash": "5863771213375512760" }, "name": "Azure SQL Server Keys", "description": "This module deploys an Azure SQL Server Key.", diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 64a65e9254..50bda35805 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -147,6 +147,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.sql-server.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -206,14 +217,10 @@ resource server_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(loc } resource server_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(server.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(server.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -256,7 +263,7 @@ module server_databases 'database/main.bicep' = [ : '' sampleName: contains(database, 'sampleName') ? database.sampleName : '' tags: database.?tags ?? tags - zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : false + zoneRedundant: contains(database, 'zoneRedundant') ? database.zoneRedundant : true elasticPoolId: contains(database, 'elasticPoolId') ? database.elasticPoolId : '' backupShortTermRetentionPolicy: contains(database, 'backupShortTermRetentionPolicy') ? database.backupShortTermRetentionPolicy @@ -298,14 +305,14 @@ module server_elasticPools 'elastic-pool/main.bicep' = [ skuCapacity: contains(elasticPool, 'skuCapacity') ? elasticPool.skuCapacity : 2 skuName: contains(elasticPool, 'skuName') ? elasticPool.skuName : 'GP_Gen5' skuTier: contains(elasticPool, 'skuTier') ? elasticPool.skuTier : 'GeneralPurpose' - zoneRedundant: contains(elasticPool, 'zoneRedundant') ? elasticPool.zoneRedundant : false + zoneRedundant: contains(elasticPool, 'zoneRedundant') ? elasticPool.zoneRedundant : true location: location tags: elasticPool.?tags ?? tags } } ] -module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module server_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-server-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -521,6 +528,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index c0cd6f3c46..9bf2e9b5c3 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2255420579130669431" + "version": "0.29.47.4906", + "templateHash": "8727418248832787909" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -66,6 +66,13 @@ "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": { @@ -587,6 +594,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -660,20 +674,20 @@ "server_roleAssignments": { "copy": { "name": "server_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Sql/servers/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Sql/servers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Sql/servers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "server" @@ -726,7 +740,7 @@ "tags": { "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" }, - "zoneRedundant": "[if(contains(parameters('databases')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('databases')[copyIndex()].zoneRedundant), createObject('value', false()))]", + "zoneRedundant": "[if(contains(parameters('databases')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('databases')[copyIndex()].zoneRedundant), createObject('value', true()))]", "elasticPoolId": "[if(contains(parameters('databases')[copyIndex()], 'elasticPoolId'), createObject('value', parameters('databases')[copyIndex()].elasticPoolId), createObject('value', ''))]", "backupShortTermRetentionPolicy": "[if(contains(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicy'), createObject('value', parameters('databases')[copyIndex()].backupShortTermRetentionPolicy), createObject('value', createObject()))]", "backupLongTermRetentionPolicy": "[if(contains(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicy'), createObject('value', parameters('databases')[copyIndex()].backupLongTermRetentionPolicy), createObject('value', createObject()))]", @@ -743,8 +757,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9732417431098147370" + "version": "0.29.47.4906", + "templateHash": "6019999815954957727" }, "name": "SQL Server Database", "description": "This module deploys an Azure SQL Server Database.", @@ -955,7 +969,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this database is zone redundant." } @@ -1221,8 +1235,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2539756678143412969" + "version": "0.29.47.4906", + "templateHash": "8635162595153731245" }, "name": "Azure SQL Server Database Short Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Short-Term Backup Retention Policy.", @@ -1323,8 +1337,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10544886139268607584" + "version": "0.29.47.4906", + "templateHash": "2778016138108001251" }, "name": "SQL Server Database Long Term Backup Retention Policies", "description": "This module deploys an Azure SQL Server Database Long-Term Backup Retention Policy.", @@ -1486,7 +1500,7 @@ "skuCapacity": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuCapacity'), createObject('value', parameters('elasticPools')[copyIndex()].skuCapacity), createObject('value', 2))]", "skuName": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuName'), createObject('value', parameters('elasticPools')[copyIndex()].skuName), createObject('value', 'GP_Gen5'))]", "skuTier": "[if(contains(parameters('elasticPools')[copyIndex()], 'skuTier'), createObject('value', parameters('elasticPools')[copyIndex()].skuTier), createObject('value', 'GeneralPurpose'))]", - "zoneRedundant": "[if(contains(parameters('elasticPools')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('elasticPools')[copyIndex()].zoneRedundant), createObject('value', false()))]", + "zoneRedundant": "[if(contains(parameters('elasticPools')[copyIndex()], 'zoneRedundant'), createObject('value', parameters('elasticPools')[copyIndex()].zoneRedundant), createObject('value', true()))]", "location": { "value": "[parameters('location')]" }, @@ -1501,8 +1515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9517065595416154313" + "version": "0.29.47.4906", + "templateHash": "18037703368269722870" }, "name": "SQL Server Elastic Pool", "description": "This module deploys an Azure SQL Server Elastic Pool.", @@ -1611,7 +1625,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones." } @@ -1752,8 +1766,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1765,6 +1779,13 @@ "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": { @@ -1868,13 +1889,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1908,8 +1929,11 @@ "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." + "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": { @@ -1949,8 +1973,11 @@ "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." + "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": { @@ -2100,6 +2127,13 @@ } }, "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')]", @@ -2117,8 +2151,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2136,7 +2170,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -2177,20 +2211,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2223,8 +2257,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -2270,7 +2304,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -2334,14 +2368,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2379,8 +2420,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5065747305708606737" + "version": "0.29.47.4906", + "templateHash": "7779473510493338097" }, "name": "Azure SQL Server Firewall Rule", "description": "This module deploys an Azure SQL Server Firewall Rule.", @@ -2485,8 +2526,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "527253335331111465" + "version": "0.29.47.4906", + "templateHash": "7859066741604114060" }, "name": "Azure SQL Server Virtual Network Rules", "description": "This module deploys an Azure SQL Server Virtual Network Rule.", @@ -2593,8 +2634,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1399980558989261994" + "version": "0.29.47.4906", + "templateHash": "6025191760768766090" }, "name": "Azure SQL Server Security Alert Policies", "description": "This module deploys an Azure SQL Server Security Alert Policy.", @@ -2744,8 +2785,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3806468899002194619" + "version": "0.29.47.4906", + "templateHash": "5682596516926040129" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -2847,8 +2888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13426110187156530306" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { @@ -2936,8 +2977,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15985945273078840265" + "version": "0.29.47.4906", + "templateHash": "5863771213375512760" }, "name": "Azure SQL Server Keys", "description": "This module deploys an Azure SQL Server Key.", @@ -3055,8 +3096,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2864913755091701450" + "version": "0.29.47.4906", + "templateHash": "6914924378490463775" }, "name": "Azure SQL Server Encryption Protector", "description": "This module deploys an Azure SQL Server Encryption Protector.", @@ -3188,8 +3229,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7842020933584780357" + "version": "0.29.47.4906", + "templateHash": "4165841300638093382" }, "name": "Azure SQL Server Audit Settings", "description": "This module deploys an Azure SQL Server Audit Settings.", @@ -3327,8 +3368,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13426110187156530306" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { diff --git a/avm/res/sql/server/security-alert-policy/main.json b/avm/res/sql/server/security-alert-policy/main.json index a815606446..8ac3df6310 100644 --- a/avm/res/sql/server/security-alert-policy/main.json +++ b/avm/res/sql/server/security-alert-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1399980558989261994" + "version": "0.29.47.4906", + "templateHash": "6025191760768766090" }, "name": "Azure SQL Server Security Alert Policies", "description": "This module deploys an Azure SQL Server Security Alert Policy.", diff --git a/avm/res/sql/server/tests/e2e/max/main.test.bicep b/avm/res/sql/server/tests/e2e/max/main.test.bicep index 92abcc0971..5487641c7f 100644 --- a/avm/res/sql/server/tests/e2e/max/main.test.bicep +++ b/avm/res/sql/server/tests/e2e/max/main.test.bicep @@ -79,11 +79,13 @@ module testDeployment '../../../main.bicep' = { location: resourceLocation roleAssignments: [ { + name: '7027a5c5-d1b1-49e0-80cc-ffdff3a3ada9' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/sql/server/version.json b/avm/res/sql/server/version.json index 3f863a2bec..9ed3662aba 100644 --- a/avm/res/sql/server/version.json +++ b/avm/res/sql/server/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/sql/server/virtual-network-rule/main.json b/avm/res/sql/server/virtual-network-rule/main.json index a6cb4ff0e5..6e94f437e0 100644 --- a/avm/res/sql/server/virtual-network-rule/main.json +++ b/avm/res/sql/server/virtual-network-rule/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "527253335331111465" + "version": "0.29.47.4906", + "templateHash": "7859066741604114060" }, "name": "Azure SQL Server Virtual Network Rules", "description": "This module deploys an Azure SQL Server Virtual Network Rule.", diff --git a/avm/res/sql/server/vulnerability-assessment/main.json b/avm/res/sql/server/vulnerability-assessment/main.json index 1c93af5772..431b5c318c 100644 --- a/avm/res/sql/server/vulnerability-assessment/main.json +++ b/avm/res/sql/server/vulnerability-assessment/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3806468899002194619" + "version": "0.29.47.4906", + "templateHash": "5682596516926040129" }, "name": "Azure SQL Server Vulnerability Assessments", "description": "This module deploys an Azure SQL Server Vulnerability Assessment.", @@ -107,8 +107,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13426110187156530306" + "version": "0.29.47.4906", + "templateHash": "17251889896692066430" } }, "parameters": { diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 2c22937da7..c1568e1e9b 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -19,8 +19,9 @@ This module deploys a Storage Account. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | @@ -46,12 +47,13 @@ The following section provides usage examples for the module, which were used to - [Deploying as a Block Blob Storage](#example-2-deploying-as-a-block-blob-storage) - [Using only changefeed configuration](#example-3-using-only-changefeed-configuration) - [Using only defaults](#example-4-using-only-defaults) -- [Using large parameter set](#example-5-using-large-parameter-set) -- [Deploying with a NFS File Share](#example-6-deploying-with-a-nfs-file-share) -- [Using Customer-Managed-Keys with System-Assigned identity](#example-7-using-customer-managed-keys-with-system-assigned-identity) -- [Using Customer-Managed-Keys with User-Assigned identity](#example-8-using-customer-managed-keys-with-user-assigned-identity) -- [Deploying as Storage Account version 1](#example-9-deploying-as-storage-account-version-1) -- [WAF-aligned](#example-10-waf-aligned) +- [Deploying with a key vault reference to save secrets](#example-5-deploying-with-a-key-vault-reference-to-save-secrets) +- [Using large parameter set](#example-6-using-large-parameter-set) +- [Deploying with a NFS File Share](#example-7-deploying-with-a-nfs-file-share) +- [Using Customer-Managed-Keys with System-Assigned identity](#example-8-using-customer-managed-keys-with-system-assigned-identity) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-9-using-customer-managed-keys-with-user-assigned-identity) +- [Deploying as Storage Account version 1](#example-10-deploying-as-storage-account-version-1) +- [WAF-aligned](#example-11-waf-aligned) ### Example 1: _Deploying as a Blob Storage_ @@ -287,7 +289,71 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 5: _Using large parameter set_ +### Example 5: _Deploying with a key vault reference to save secrets_ + +This instance deploys the module saving all its secrets in a key vault. + + +

+ +via Bicep module + +```bicep +module storageAccount 'br/public:avm/res/storage/storage-account:' = { + name: 'storageAccountDeployment' + params: { + // Required parameters + name: 'kvref' + // Non-required parameters + location: '' + secretsExportConfiguration: { + accessKey1: 'custom-key1-name' + accessKey2: 'custom-key2-name' + connectionString1: 'custom-connectionString1-name' + connectionString2: 'custom-connectionString2-name' + keyVaultResourceId: '' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "kvref" + }, + // Non-required parameters + "location": { + "value": "" + }, + "secretsExportConfiguration": { + "value": { + "accessKey1": "custom-key1-name", + "accessKey2": "custom-key2-name", + "connectionString1": "custom-connectionString1-name", + "connectionString2": "custom-connectionString2-name", + "keyVaultResourceId": "" + } + } + } +} +``` + +
+

+ +### Example 6: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -399,11 +465,13 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { name: 'avdprofiles' roleAssignments: [ { + name: 'cff1213b-7877-4425-b67c-bb1de8950dfb' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -612,11 +680,13 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { requireInfrastructureEncryption: true roleAssignments: [ { + name: '30b99723-a3d8-4e31-8872-b80c960d62bd' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -822,11 +892,13 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { "name": "avdprofiles", "roleAssignments": [ { + "name": "cff1213b-7877-4425-b67c-bb1de8950dfb", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1057,11 +1129,13 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { "roleAssignments": { "value": [ { + "name": "30b99723-a3d8-4e31-8872-b80c960d62bd", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -1153,7 +1227,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 6: _Deploying with a NFS File Share_ +### Example 7: _Deploying with a NFS File Share_ This instance deploys the module with a NFS File Share. @@ -1227,7 +1301,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 7: _Using Customer-Managed-Keys with System-Assigned identity_ +### Example 8: _Using Customer-Managed-Keys with System-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret. @@ -1331,7 +1405,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 8: _Using Customer-Managed-Keys with User-Assigned identity_ +### Example 9: _Using Customer-Managed-Keys with User-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. @@ -1451,7 +1525,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 9: _Deploying as Storage Account version 1_ +### Example 10: _Deploying as Storage Account version 1_ This instance deploys the module as Storage Account version 1. @@ -1503,7 +1577,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = {

-### Example 10: _WAF-aligned_ +### Example 11: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -2110,6 +2184,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { | [`requireInfrastructureEncryption`](#parameter-requireinfrastructureencryption) | bool | 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. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`sasExpirationPeriod`](#parameter-sasexpirationperiod) | string | The SAS expiration period. DD.HH:MM:SS. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | | [`skuName`](#parameter-skuname) | string | Storage Account Sku Name. | | [`supportsHttpsTrafficOnly`](#parameter-supportshttpstrafficonly) | bool | Allows HTTPS traffic only to storage service if sets to true. | | [`tableServices`](#parameter-tableservices) | object | Table service and tables to create. | @@ -2952,6 +3027,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -3002,6 +3078,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -3080,6 +3163,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -3130,6 +3214,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -3155,6 +3246,63 @@ The SAS expiration period. DD.HH:MM:SS. - Type: string - Default: `''` +### Parameter: `secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The key vault name where to store the keys and connection strings generated by the modules. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessKey1`](#parameter-secretsexportconfigurationaccesskey1) | string | The accessKey1 secret name to create. | +| [`accessKey2`](#parameter-secretsexportconfigurationaccesskey2) | string | The accessKey2 secret name to create. | +| [`connectionString1`](#parameter-secretsexportconfigurationconnectionstring1) | string | The connectionString1 secret name to create. | +| [`connectionString2`](#parameter-secretsexportconfigurationconnectionstring2) | string | The connectionString2 secret name to create. | + +### Parameter: `secretsExportConfiguration.keyVaultResourceId` + +The key vault name where to store the keys and connection strings generated by the modules. + +- Required: Yes +- Type: string + +### Parameter: `secretsExportConfiguration.accessKey1` + +The accessKey1 secret name to create. + +- Required: No +- Type: string + +### Parameter: `secretsExportConfiguration.accessKey2` + +The accessKey2 secret name to create. + +- Required: No +- Type: string + +### Parameter: `secretsExportConfiguration.connectionString1` + +The connectionString1 secret name to create. + +- Required: No +- Type: string + +### Parameter: `secretsExportConfiguration.connectionString2` + +The connectionString2 secret name to create. + +- Required: No +- Type: string + ### Parameter: `skuName` Storage Account Sku Name. @@ -3204,6 +3352,7 @@ Tags of the resource. | Output | Type | Description | | :-- | :-- | :-- | +| `exportedSecrets` | | A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the deployed storage account. | | `primaryBlobEndpoint` | string | The primary blob endpoint reference if blob services are deployed. | @@ -3218,7 +3367,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Notes diff --git a/avm/res/storage/storage-account/blob-service/container/README.md b/avm/res/storage/storage-account/blob-service/container/README.md index e623d10e13..d386db3d55 100644 --- a/avm/res/storage/storage-account/blob-service/container/README.md +++ b/avm/res/storage/storage-account/blob-service/container/README.md @@ -162,6 +162,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -212,6 +213,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index 280bbf8788..e92ebe5e3f 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2543276032744560941" + "version": "0.29.47.4906", + "templateHash": "7245741358008626948" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/container/main.bicep b/avm/res/storage/storage-account/blob-service/container/main.bicep index 5fa30ec27a..fd1c851da3 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.bicep +++ b/avm/res/storage/storage-account/blob-service/container/main.bicep @@ -90,6 +90,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = { name: storageAccountName @@ -128,14 +139,10 @@ module immutabilityPolicy 'immutability-policy/main.bicep' = if (!empty((immutab } resource container_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(container.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(container.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -161,6 +168,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index 17e9500dc8..c2b8aebe7d 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7045309160947869799" + "version": "0.29.47.4906", + "templateHash": "2376441074312126168" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -18,6 +18,13 @@ "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": { @@ -170,6 +177,13 @@ } }, "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')]", @@ -222,20 +236,20 @@ "container_roleAssignments": { "copy": { "name": "container_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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'), 'default', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -274,8 +288,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2543276032744560941" + "version": "0.29.47.4906", + "templateHash": "7245741358008626948" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 035fa68ac2..2a053636e7 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2306287879023715578" + "version": "0.29.47.4906", + "templateHash": "6152214765227449988" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -403,8 +403,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7045309160947869799" + "version": "0.29.47.4906", + "templateHash": "2376441074312126168" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -416,6 +416,13 @@ "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": { @@ -568,6 +575,13 @@ } }, "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')]", @@ -620,20 +634,20 @@ "container_roleAssignments": { "copy": { "name": "container_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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'), 'default', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -672,8 +686,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2543276032744560941" + "version": "0.29.47.4906", + "templateHash": "7245741358008626948" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 747bccd787..4e9d267c82 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7463227074634701879" + "version": "0.29.47.4906", + "templateHash": "11112322068652007329" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -286,8 +286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1342480740201032357" + "version": "0.29.47.4906", + "templateHash": "7774942141784896900" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -299,6 +299,13 @@ "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": { @@ -486,8 +493,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8779226603522513073" + "version": "0.29.47.4906", + "templateHash": "17818484349715313082" } }, "parameters": { @@ -505,6 +512,13 @@ } }, "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)))))]" + } + ], "$fxv#0": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", @@ -617,7 +631,7 @@ { "copy": { "name": "fileShare_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2021-04-01", @@ -633,28 +647,28 @@ "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" }, "name": { - "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(parameters('fileShareResourceId'), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId, 'tyfa'))]" }, "roleDefinitionId": { - "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" }, "principalId": { - "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" }, "principalType": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" }, "description": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" }, "condition": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" }, "conditionVersion": { - "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + "value": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" }, "delegatedManagedIdentityResourceId": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" } } } diff --git a/avm/res/storage/storage-account/file-service/share/README.md b/avm/res/storage/storage-account/file-service/share/README.md index dc44e02011..5cd3c558df 100644 --- a/avm/res/storage/storage-account/file-service/share/README.md +++ b/avm/res/storage/storage-account/file-service/share/README.md @@ -117,6 +117,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -167,6 +168,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/storage/storage-account/file-service/share/main.bicep b/avm/res/storage/storage-account/file-service/share/main.bicep index dc84824652..b626c5c212 100644 --- a/avm/res/storage/storage-account/file-service/share/main.bicep +++ b/avm/res/storage/storage-account/file-service/share/main.bicep @@ -84,6 +84,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index e65aa45d20..0d4365a4a4 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1342480740201032357" + "version": "0.29.47.4906", + "templateHash": "7774942141784896900" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -18,6 +18,13 @@ "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": { @@ -205,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8779226603522513073" + "version": "0.29.47.4906", + "templateHash": "17818484349715313082" } }, "parameters": { @@ -224,6 +231,13 @@ } }, "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)))))]" + } + ], "$fxv#0": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", @@ -336,7 +350,7 @@ { "copy": { "name": "fileShare_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2021-04-01", @@ -352,28 +366,28 @@ "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" }, "name": { - "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(parameters('fileShareResourceId'), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId, 'tyfa'))]" }, "roleDefinitionId": { - "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" }, "principalId": { - "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" }, "principalType": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" }, "description": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" }, "condition": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" }, "conditionVersion": { - "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + "value": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" }, "delegatedManagedIdentityResourceId": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" } } } diff --git a/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep b/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep index 22689771c3..bb16f5bd94 100644 --- a/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep +++ b/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep @@ -46,9 +46,20 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource fileShare_roleAssignments 'Microsoft.Resources/deployments@2021-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { name: '${uniqueString(deployment().name)}-Share-Rbac-${index}' properties: { mode: 'Incremental' @@ -61,17 +72,15 @@ resource fileShare_roleAssignments 'Microsoft.Resources/deployments@2021-04-01' value: replace(fileShareResourceId, '/shares/', '/fileShares/') } name: { - value: guid(fileShareResourceId, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName, 'tyfa') + value: roleAssignment.?name ?? guid( + fileShareResourceId, + roleAssignment.principalId, + roleAssignment.roleDefinitionId, + 'tyfa' + ) } roleDefinitionId: { - value: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - roleAssignment.roleDefinitionIdOrName - ) + value: roleAssignment.roleDefinitionId } principalId: { value: roleAssignment.principalId diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index ed785baf66..3514e02614 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14968464858285923305" + "version": "0.29.47.4906", + "templateHash": "18130658711251621530" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 9e66def864..5ab00a6585 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -180,6 +180,9 @@ param sasExpirationPeriod string = '' ]) param keyType string? +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? + var supportsBlobService = kind == 'BlockBlobStorage' || kind == 'BlobStorage' || kind == 'StorageV2' || kind == 'Storage' var supportsFileService = kind == 'FileStorage' || kind == 'StorageV2' || kind == 'Storage' @@ -238,6 +241,14 @@ var builtInRoleNames = { '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' @@ -280,6 +291,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.storage-storageaccount.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -455,14 +477,10 @@ resource storageAccount_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e } resource storageAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(storageAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(storageAccount.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -474,7 +492,7 @@ resource storageAccount_roleAssignments 'Microsoft.Authorization/roleAssignments } ] -module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module storageAccount_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-storageAccount-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -613,6 +631,52 @@ module storageAccount_tableServices 'table-service/main.bicep' = if (!empty(tabl } } +module secretsExport 'modules/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!, 'accessKey1') + ? [ + { + name: secretsExportConfiguration!.accessKey1 + value: storageAccount.listKeys().keys[0].value + } + ] + : [], + contains(secretsExportConfiguration!, 'connectionString1') + ? [ + { + name: secretsExportConfiguration!.connectionString1 + value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net' + } + ] + : [], + contains(secretsExportConfiguration!, 'accessKey2') + ? [ + { + name: secretsExportConfiguration!.accessKey2 + value: storageAccount.listKeys().keys[1].value + } + ] + : [], + contains(secretsExportConfiguration!, 'connectionString2') + ? [ + { + name: secretsExportConfiguration!.connectionString2 + value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[1].value};EndpointSuffix=core.windows.net' + } + ] + : [] + ) + } +} + @description('The resource ID of the deployed storage account.') output resourceId string = storageAccount.id @@ -636,6 +700,11 @@ output location string = storageAccount.location @description('All service endpoints of the deployed storage account, Note Standard_LRS and Standard_ZRS accounts only have a blob service endpoint.') output serviceEndpoints object = storageAccount.properties.primaryEndpoints +@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) + : {} + // =============== // // Definitions // // =============== // @@ -657,6 +726,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -833,3 +905,26 @@ type customerManagedKeyType = { @description('Optional. User assigned identity to use when fetching the customer managed key. If used must also be specified in `managedIdentities.userAssignedResourceIds`. Required if no system assigned identity is available for use.') userAssignedIdentityResourceId: string? }? + +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 accessKey1 secret name to create.') + accessKey1: string? + + @description('Optional. The connectionString1 secret name to create.') + connectionString1: string? + + @description('Optional. The accessKey2 secret name to create.') + accessKey2: string? + + @description('Optional. The connectionString2 secret name to create.') + connectionString2: string? +} + +import { secretSetType } from 'modules/keyVaultExport.bicep' +type secretsOutputType = { + @description('An exported secret\'s references.') + *: secretSetType +} diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index e198faf350..effc366536 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "5769970568658743015" + "version": "0.29.47.4906", + "templateHash": "5346924083995287195" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -66,6 +66,13 @@ "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": { @@ -512,6 +519,77 @@ } }, "nullable": 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." + } + }, + "accessKey1": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey1 secret name to create." + } + }, + "connectionString1": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString1 secret name to create." + } + }, + "accessKey2": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey2 secret name to create." + } + }, + "connectionString2": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString2 secret name to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "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": { @@ -838,9 +916,23 @@ "metadata": { "description": "Optional. The keyType to use with Queue & Table services." } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", @@ -858,6 +950,8 @@ "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')]", @@ -1011,20 +1105,20 @@ "storageAccount_roleAssignments": { "copy": { "name": "storageAccount_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1094,8 +1188,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1107,6 +1201,13 @@ "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": { @@ -1210,13 +1311,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1250,8 +1351,11 @@ "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." + "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": { @@ -1291,8 +1395,11 @@ "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." + "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": { @@ -1442,6 +1549,13 @@ } }, "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')]", @@ -1459,8 +1573,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1478,7 +1592,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1519,20 +1633,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1565,8 +1679,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1612,7 +1726,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -1676,14 +1790,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -1716,8 +1837,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9473195527943694039" + "version": "0.29.47.4906", + "templateHash": "11289787713365096902" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", @@ -1826,8 +1947,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14968464858285923305" + "version": "0.29.47.4906", + "templateHash": "18130658711251621530" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", @@ -2044,8 +2165,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2306287879023715578" + "version": "0.29.47.4906", + "templateHash": "6152214765227449988" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2442,8 +2563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7045309160947869799" + "version": "0.29.47.4906", + "templateHash": "2376441074312126168" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2455,6 +2576,13 @@ "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": { @@ -2607,6 +2735,13 @@ } }, "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')]", @@ -2659,20 +2794,20 @@ "container_roleAssignments": { "copy": { "name": "container_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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'), 'default', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -2711,8 +2846,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2543276032744560941" + "version": "0.29.47.4906", + "templateHash": "7245741358008626948" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", @@ -2890,8 +3025,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7463227074634701879" + "version": "0.29.47.4906", + "templateHash": "11112322068652007329" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3171,8 +3306,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "1342480740201032357" + "version": "0.29.47.4906", + "templateHash": "7774942141784896900" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3184,6 +3319,13 @@ "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": { @@ -3371,8 +3513,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8779226603522513073" + "version": "0.29.47.4906", + "templateHash": "17818484349715313082" } }, "parameters": { @@ -3390,6 +3532,13 @@ } }, "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)))))]" + } + ], "$fxv#0": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", @@ -3502,7 +3651,7 @@ { "copy": { "name": "fileShare_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2021-04-01", @@ -3518,28 +3667,28 @@ "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" }, "name": { - "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(parameters('fileShareResourceId'), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId, 'tyfa'))]" }, "roleDefinitionId": { - "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" }, "principalId": { - "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" }, "principalType": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" }, "description": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" }, "condition": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" }, "conditionVersion": { - "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + "value": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" }, "delegatedManagedIdentityResourceId": { - "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" } } } @@ -3640,8 +3789,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10678250016540336570" + "version": "0.29.47.4906", + "templateHash": "11962188257977966881" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -3885,8 +4034,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13487964166280180730" + "version": "0.29.47.4906", + "templateHash": "18160215842105253661" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -3898,6 +4047,13 @@ "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": { @@ -3989,6 +4145,13 @@ } }, "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')]", @@ -4035,20 +4198,20 @@ "queue_roleAssignments": { "copy": { "name": "queue_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -4142,8 +4305,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16839054392438941735" + "version": "0.29.47.4906", + "templateHash": "11825608650296439803" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4384,8 +4547,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3177845984945141330" + "version": "0.29.47.4906", + "templateHash": "16189309416113928106" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -4397,6 +4560,13 @@ "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": { @@ -4481,6 +4651,13 @@ } }, "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')]", @@ -4522,20 +4699,20 @@ "table_roleAssignments": { "copy": { "name": "table_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -4600,6 +4777,140 @@ "dependsOn": [ "storageAccount" ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString1, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0].value))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString2, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1].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.29.47.4906", + "templateHash": "986606208324987345" + } + }, + "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": "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]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "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": [ + "storageAccount" + ] } }, "outputs": { @@ -4651,6 +4962,13 @@ "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]" + }, + "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())]" } } } \ No newline at end of file diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index c8c8f8d02a..c348d69daf 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9473195527943694039" + "version": "0.29.47.4906", + "templateHash": "11289787713365096902" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", diff --git a/avm/res/storage/storage-account/modules/keyVaultExport.bicep b/avm/res/storage/storage-account/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..d537d2407e --- /dev/null +++ b/avm/res/storage/storage-account/modules/keyVaultExport.bicep @@ -0,0 +1,62 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Key Vault to set the ecrets in.') +param keyVaultName string + +@description('Required. The secrets to set in the Key Vault.') +param secretsToSet secretToSetType[] + +// ============= // +// Resources // +// ============= // + +resource keyVault 'Microsoft.KeyVault/vaults@2022-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 // +// =========== // + +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetType[] = [ + #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 + } +] + +// =============== // +// Definitions // +// =============== // + +@export() +type secretSetType = { + @description('The resourceId of the exported secret.') + secretResourceId: string + + @description('The secret URI of the exported secret.') + secretUri: string +} + +type secretToSetType = { + @description('Required. The name of the secret to set.') + name: string + + @description('Required. The value of the secret to set.') + @secure() + value: string +} diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index b24d4cfa81..535642de39 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10678250016540336570" + "version": "0.29.47.4906", + "templateHash": "11962188257977966881" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -250,8 +250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13487964166280180730" + "version": "0.29.47.4906", + "templateHash": "18160215842105253661" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -263,6 +263,13 @@ "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": { @@ -354,6 +361,13 @@ } }, "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')]", @@ -400,20 +414,20 @@ "queue_roleAssignments": { "copy": { "name": "queue_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/storage/storage-account/queue-service/queue/README.md b/avm/res/storage/storage-account/queue-service/queue/README.md index 760a6f70a4..8e24537f5e 100644 --- a/avm/res/storage/storage-account/queue-service/queue/README.md +++ b/avm/res/storage/storage-account/queue-service/queue/README.md @@ -82,6 +82,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -132,6 +133,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/storage/storage-account/queue-service/queue/main.bicep b/avm/res/storage/storage-account/queue-service/queue/main.bicep index 9b1aba392c..3dbc2a173d 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.bicep +++ b/avm/res/storage/storage-account/queue-service/queue/main.bicep @@ -61,6 +61,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = { name: storageAccountName @@ -78,14 +89,10 @@ resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2023-04-0 } resource queue_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(queue.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(queue.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -111,6 +118,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 73e53bf179..9472e55002 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "13487964166280180730" + "version": "0.29.47.4906", + "templateHash": "18160215842105253661" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -18,6 +18,13 @@ "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": { @@ -109,6 +116,13 @@ } }, "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')]", @@ -155,20 +169,20 @@ "queue_roleAssignments": { "copy": { "name": "queue_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index 23ca6f59a0..c1ec65c05a 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16839054392438941735" + "version": "0.29.47.4906", + "templateHash": "11825608650296439803" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -247,8 +247,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3177845984945141330" + "version": "0.29.47.4906", + "templateHash": "16189309416113928106" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -260,6 +260,13 @@ "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": { @@ -344,6 +351,13 @@ } }, "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')]", @@ -385,20 +399,20 @@ "table_roleAssignments": { "copy": { "name": "table_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/storage/storage-account/table-service/table/README.md b/avm/res/storage/storage-account/table-service/table/README.md index d3b39dff00..03239bdafa 100644 --- a/avm/res/storage/storage-account/table-service/table/README.md +++ b/avm/res/storage/storage-account/table-service/table/README.md @@ -73,6 +73,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -123,6 +124,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/storage/storage-account/table-service/table/main.bicep b/avm/res/storage/storage-account/table-service/table/main.bicep index 6553a048a3..78ba2ed9e0 100644 --- a/avm/res/storage/storage-account/table-service/table/main.bicep +++ b/avm/res/storage/storage-account/table-service/table/main.bicep @@ -50,6 +50,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = { name: storageAccountName @@ -64,14 +75,10 @@ resource table 'Microsoft.Storage/storageAccounts/tableServices/tables@2023-04-0 } resource table_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(table.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(table.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -97,6 +104,9 @@ output resourceGroupName string = resourceGroup().name // =============== // type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index 30cce3e944..5a86eccbdd 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3177845984945141330" + "version": "0.29.47.4906", + "templateHash": "16189309416113928106" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -18,6 +18,13 @@ "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": { @@ -102,6 +109,13 @@ } }, "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')]", @@ -143,20 +157,20 @@ "table_roleAssignments": { "copy": { "name": "table_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "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": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" diff --git a/avm/res/storage/storage-account/tests/e2e/kvSecrets/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/kvSecrets/dependencies.bicep new file mode 100644 index 0000000000..61c051d86d --- /dev/null +++ b/avm/res/storage/storage-account/tests/e2e/kvSecrets/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param keyVaultName string + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + tenantId: subscription().tenantId + } +} + +@description('The name of the Key Vault created.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/storage/storage-account/tests/e2e/kvSecrets/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/kvSecrets/main.test.bicep new file mode 100644 index 0000000000..6efab286b0 --- /dev/null +++ b/avm/res/storage/storage-account/tests/e2e/kvSecrets/main.test.bicep @@ -0,0 +1,58 @@ +targetScope = 'subscription' + +metadata name = 'Deploying with a key vault reference to save secrets' +metadata description = 'This instance deploys the module saving all its secrets in a key vault.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-storage.storageaccount-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ssakvs' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============== // +// General resources +// ============== // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + location: resourceLocation + name: '${namePrefix}kvref' + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + accessKey1: 'custom-key1-name' + accessKey2: 'custom-key2-name' + connectionString1: 'custom-connectionString1-name' + connectionString2: 'custom-connectionString2-name' + } + } +} diff --git a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep index 78a91693b2..b0751eec2b 100644 --- a/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/max/main.test.bicep @@ -255,11 +255,13 @@ module testDeployment '../../../main.bicep' = [ shareQuota: 5120 roleAssignments: [ { + name: 'cff1213b-7877-4425-b67c-bb1de8950dfb' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}-share-avdprofiles') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -402,11 +404,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '30b99723-a3d8-4e31-8872-b80c960d62bd' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 6a120cace8..7466cbe674 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.11", - "pathFilters": [ - "./main.json" - ] -} \ No newline at end of file + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.13", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/synapse/private-link-hub/README.md b/avm/res/synapse/private-link-hub/README.md index 0fe999a92c..1ccae595b7 100644 --- a/avm/res/synapse/private-link-hub/README.md +++ b/avm/res/synapse/private-link-hub/README.md @@ -22,8 +22,8 @@ This module deploys an Azure Synapse Analytics (Private Link Hub). | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Synapse/privateLinkHubs` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/privateLinkHubs) | ## Usage examples @@ -128,11 +128,13 @@ module privateLinkHub 'br/public:avm/res/synapse/private-link-hub:' = { ] roleAssignments: [ { + name: '049a8b5a-70dc-4749-965c-b009733cf432' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Reader' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -197,11 +199,13 @@ module privateLinkHub 'br/public:avm/res/synapse/private-link-hub:' = { "roleAssignments": { "value": [ { + "name": "049a8b5a-70dc-4749-965c-b009733cf432", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Reader" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -653,6 +657,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -703,6 +708,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -756,6 +768,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -806,6 +819,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -846,7 +866,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Data Collection diff --git a/avm/res/synapse/private-link-hub/main.bicep b/avm/res/synapse/private-link-hub/main.bicep index 40ea18256f..863538dd6e 100644 --- a/avm/res/synapse/private-link-hub/main.bicep +++ b/avm/res/synapse/private-link-hub/main.bicep @@ -37,6 +37,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.synapse-privatelinkhub.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -76,14 +87,10 @@ resource privateLinkHub_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!e // RBAC resource privateLinkHub_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(privateLinkHub.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(privateLinkHub.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -96,7 +103,7 @@ resource privateLinkHub_roleAssignments 'Microsoft.Authorization/roleAssignments ] // Private Endpoints -module privateLinkHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module privateLinkHub_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-privateLinkHub-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -174,6 +181,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/synapse/private-link-hub/main.json b/avm/res/synapse/private-link-hub/main.json index 0930cfd2b4..0990e1175b 100644 --- a/avm/res/synapse/private-link-hub/main.json +++ b/avm/res/synapse/private-link-hub/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9680544353461216711" + "version": "0.29.47.4906", + "templateHash": "14426311365279116050" }, "name": "Azure Synapse Analytics", "description": "This module deploys an Azure Synapse Analytics (Private Link Hub).", @@ -43,6 +43,13 @@ "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": { @@ -350,6 +357,13 @@ } }, "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')]", @@ -403,20 +417,20 @@ "privateLinkHub_roleAssignments": { "copy": { "name": "privateLinkHub_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Synapse/privateLinkHubs/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Synapse/privateLinkHubs', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "privateLinkHub" @@ -486,8 +500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -499,6 +513,13 @@ "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": { @@ -602,13 +623,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -642,8 +663,11 @@ "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." + "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": { @@ -683,8 +707,11 @@ "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." + "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": { @@ -834,6 +861,13 @@ } }, "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')]", @@ -851,8 +885,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -870,7 +904,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -911,20 +945,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -957,8 +991,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1004,7 +1038,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -1068,14 +1102,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } diff --git a/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep b/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep index 314df85fbd..2b390c768e 100644 --- a/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep +++ b/avm/res/synapse/private-link-hub/tests/e2e/max/main.test.bicep @@ -79,11 +79,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '049a8b5a-70dc-4749-965c-b009733cf432' roleDefinitionIdOrName: 'Reader' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') principalId: nestedDependencies.outputs.managedIdentityPrincipalId roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' principalType: 'ServicePrincipal' diff --git a/avm/res/synapse/private-link-hub/version.json b/avm/res/synapse/private-link-hub/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/synapse/private-link-hub/version.json +++ b/avm/res/synapse/private-link-hub/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index cbc4919670..caff6c5ab8 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -24,10 +24,11 @@ This module deploys a Synapse Workspace. | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Synapse/workspaces` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces) | | `Microsoft.Synapse/workspaces/administrators` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/administrators) | +| `Microsoft.Synapse/workspaces/firewallRules` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/firewallRules) | | `Microsoft.Synapse/workspaces/integrationRuntimes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/integrationRuntimes) | | `Microsoft.Synapse/workspaces/keys` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/keys) | @@ -42,9 +43,10 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using encryption with Customer-Managed-Key](#example-2-using-encryption-with-customer-managed-key) - [Using encryption with Customer-Managed-Key](#example-3-using-encryption-with-customer-managed-key) -- [Using managed Vnet](#example-4-using-managed-vnet) -- [Using large parameter set](#example-5-using-large-parameter-set) -- [WAF-aligned](#example-6-waf-aligned) +- [Using firewall rules](#example-4-using-firewall-rules) +- [Using managed Vnet](#example-5-using-managed-vnet) +- [Using large parameter set](#example-6-using-large-parameter-set) +- [WAF-aligned](#example-7-waf-aligned) ### Example 1: _Using only defaults_ @@ -252,7 +254,93 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 4: _Using managed Vnet_ +### Example 4: _Using firewall rules_ + +This instance deploys the module with the configuration of firewall rules. + + +

+ +via Bicep module + +```bicep +module workspace 'br/public:avm/res/synapse/workspace:' = { + name: 'workspaceDeployment' + params: { + // Required parameters + defaultDataLakeStorageAccountResourceId: '' + defaultDataLakeStorageFilesystem: '' + name: 'swfwr001' + sqlAdministratorLogin: 'synwsadmin' + // Non-required parameters + firewallRules: [ + { + endIpAddress: '87.14.134.20' + name: 'fwrule01' + startIpAddress: '87.14.134.20' + } + { + endIpAddress: '87.14.134.22' + name: 'fwrule02' + startIpAddress: '87.14.134.21' + } + ] + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "defaultDataLakeStorageAccountResourceId": { + "value": "" + }, + "defaultDataLakeStorageFilesystem": { + "value": "" + }, + "name": { + "value": "swfwr001" + }, + "sqlAdministratorLogin": { + "value": "synwsadmin" + }, + // Non-required parameters + "firewallRules": { + "value": [ + { + "endIpAddress": "87.14.134.20", + "name": "fwrule01", + "startIpAddress": "87.14.134.20" + }, + { + "endIpAddress": "87.14.134.22", + "name": "fwrule02", + "startIpAddress": "87.14.134.21" + } + ] + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Using managed Vnet_ This instance deploys the module using a managed Vnet. @@ -328,7 +416,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 5: _Using large parameter set_ +### Example 6: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -430,11 +518,13 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { ] roleAssignments: [ { + name: '499f9243-2170-4204-807d-ee6d0f94a0d0' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -575,11 +665,13 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { "roleAssignments": { "value": [ { + "name": "499f9243-2170-4204-807d-ee6d0f94a0d0", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -598,7 +690,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = {

-### Example 6: _WAF-aligned_ +### Example 7: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -779,6 +871,7 @@ module workspace 'br/public:avm/res/synapse/workspace:' = { | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`encryptionActivateWorkspace`](#parameter-encryptionactivateworkspace) | bool | Activate workspace by adding the system managed identity in the KeyVault containing the customer managed key and activating the workspace. | +| [`firewallRules`](#parameter-firewallrules) | array | List of firewall rules to be created in the workspace. | | [`initialWorkspaceAdminObjectID`](#parameter-initialworkspaceadminobjectid) | string | AAD object ID of initial workspace admin. | | [`integrationRuntimes`](#parameter-integrationruntimes) | array | The Integration Runtimes to create. | | [`linkedAccessCheckOnTargetResource`](#parameter-linkedaccesscheckontargetresource) | bool | Linked Access Check On Target Resource. | @@ -1074,6 +1167,42 @@ Activate workspace by adding the system managed identity in the KeyVault contain - Type: bool - Default: `False` +### Parameter: `firewallRules` + +List of firewall rules to be created in the workspace. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endIpAddress`](#parameter-firewallrulesendipaddress) | string | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. | +| [`name`](#parameter-firewallrulesname) | string | The name of the firewall rule. | +| [`startIpAddress`](#parameter-firewallrulesstartipaddress) | string | The start IP address of the firewall rule. Must be IPv4 format. | + +### Parameter: `firewallRules.endIpAddress` + +The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. + +- Required: Yes +- Type: string + +### Parameter: `firewallRules.name` + +The name of the firewall rule. + +- Required: Yes +- Type: string + +### Parameter: `firewallRules.startIpAddress` + +The start IP address of the firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + ### Parameter: `initialWorkspaceAdminObjectID` AAD object ID of initial workspace admin. @@ -1455,6 +1584,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -1505,6 +1635,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1574,6 +1711,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1624,6 +1762,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1681,7 +1826,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Data Collection diff --git a/avm/res/synapse/workspace/administrators/main.json b/avm/res/synapse/workspace/administrators/main.json index 76087e4632..bb9e0505cd 100644 --- a/avm/res/synapse/workspace/administrators/main.json +++ b/avm/res/synapse/workspace/administrators/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "351030244990389212" + "version": "0.29.47.4906", + "templateHash": "15890618337043813918" }, "name": "Synapse Workspaces Administrators", "description": "This module deploys Synapse Workspaces Administrators.", diff --git a/avm/res/synapse/workspace/firewall-rules/README.md b/avm/res/synapse/workspace/firewall-rules/README.md new file mode 100644 index 0000000000..0816f90472 --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/README.md @@ -0,0 +1,78 @@ +# Synapse Workspaces Firewall Rules `[Microsoft.Synapse/workspaces/firewallRules]` + +This module deploys Synapse Workspaces Firewall Rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Synapse/workspaces/firewallRules` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Synapse/2021-06-01/workspaces/firewallRules) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endIpAddress`](#parameter-endipaddress) | string | The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. | +| [`name`](#parameter-name) | string | The name of the firewall rule. | +| [`startIpAddress`](#parameter-startipaddress) | string | The start IP address of the firewall rule. Must be IPv4 format. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`workspaceName`](#parameter-workspacename) | string | The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. | + +### Parameter: `endIpAddress` + +The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. + +- Required: Yes +- Type: string + +### Parameter: `name` + +The name of the firewall rule. + +- Required: Yes +- Type: string + +### Parameter: `startIpAddress` + +The start IP address of the firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + +### Parameter: `workspaceName` + +The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed firewall rule. | +| `resourceGroupName` | string | The resource group of the deployed firewall rule. | +| `resourceId` | string | The resource ID of the deployed firewall rule. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/synapse/workspace/firewall-rules/main.bicep b/avm/res/synapse/workspace/firewall-rules/main.bicep new file mode 100644 index 0000000000..a3e12b4eca --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/main.bicep @@ -0,0 +1,37 @@ +metadata name = 'Synapse Workspaces Firewall Rules' +metadata description = 'This module deploys Synapse Workspaces Firewall Rules.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Required. The name of the firewall rule.') +param name string + +@description('Required. The start IP address of the firewall rule. Must be IPv4 format.') +param startIpAddress string + +@description('Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress.') +param endIpAddress string + +resource workspace 'Microsoft.Synapse/workspaces@2021-06-01' existing = { + name: workspaceName +} + +resource firewallRule 'Microsoft.Synapse/workspaces/firewallRules@2021-06-01' = { + name: name + parent: workspace + properties: { + startIpAddress: startIpAddress + endIpAddress: endIpAddress + } +} + +@description('The name of the deployed firewall rule.') +output name string = firewallRule.name + +@description('The resource ID of the deployed firewall rule.') +output resourceId string = firewallRule.id + +@description('The resource group of the deployed firewall rule.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/synapse/workspace/firewall-rules/main.json b/avm/res/synapse/workspace/firewall-rules/main.json new file mode 100644 index 0000000000..ce7fd0baaa --- /dev/null +++ b/avm/res/synapse/workspace/firewall-rules/main.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6729418057172891787" + }, + "name": "Synapse Workspaces Firewall Rules", + "description": "This module deploys Synapse Workspaces Firewall Rules.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/firewallRules", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed firewall rule." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/firewallRules', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed firewall rule." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/synapse/workspace/integration-runtime/main.json b/avm/res/synapse/workspace/integration-runtime/main.json index 50fe789258..07711c9fcc 100644 --- a/avm/res/synapse/workspace/integration-runtime/main.json +++ b/avm/res/synapse/workspace/integration-runtime/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12071965359188483950" + "version": "0.29.47.4906", + "templateHash": "4134219125418987684" }, "name": "Synapse Workspace Integration Runtimes", "description": "This module deploys a Synapse Workspace Integration Runtime.", diff --git a/avm/res/synapse/workspace/key/main.json b/avm/res/synapse/workspace/key/main.json index 14c8ac9883..d98324c30c 100644 --- a/avm/res/synapse/workspace/key/main.json +++ b/avm/res/synapse/workspace/key/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17207449952619386820" + "version": "0.29.47.4906", + "templateHash": "6170014210030770909" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key.", diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index 538751229e..5c3a0f2768 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -66,6 +66,9 @@ param preventDataExfiltration bool = false @description('Optional. Enable or Disable public network access to workspace.') param publicNetworkAccess string = 'Enabled' +@description('Optional. List of firewall rules to be created in the workspace.') +param firewallRules firewallRuleType[]? + @description('Optional. Purview Resource ID.') param purviewResourceID string = '' @@ -136,6 +139,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.synapse-workspace.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -303,14 +317,10 @@ resource workspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty( // RBAC resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(workspace.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -322,8 +332,21 @@ resource workspace_roleAssignments 'Microsoft.Authorization/roleAssignments@2022 } ] +// Firewall Rules +module workspace_firewallRules 'firewall-rules/main.bicep' = [ + for (rule, index) in (firewallRules ?? []): { + name: '${uniqueString(deployment().name, location)}-workspace-FirewallRule-${index}' + params: { + name: rule.name + endIpAddress: rule.endIpAddress + startIpAddress: rule.startIpAddress + workspaceName: workspace.name + } + } +] + // Endpoints -module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module workspace_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-workspace-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -434,6 +457,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string @@ -596,3 +622,14 @@ type adminType = { @secure() tenantId: string? }? + +type firewallRuleType = { + @description('Required. The name of the firewall rule.') + name: string + + @description('Required. The start IP address of the firewall rule. Must be IPv4 format.') + startIpAddress: string + + @description('Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress.') + endIpAddress: string +} diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 4b0570811e..b38d4a74e4 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16827959448836859226" + "version": "0.29.47.4906", + "templateHash": "12241893102141299283" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -58,6 +58,13 @@ "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": { @@ -465,6 +472,29 @@ } }, "nullable": true + }, + "firewallRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + } } }, "parameters": { @@ -602,6 +632,16 @@ "description": "Optional. Enable or Disable public network access to workspace." } }, + "firewallRules": { + "type": "array", + "items": { + "$ref": "#/definitions/firewallRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of firewall rules to be created in the workspace." + } + }, "purviewResourceID": { "type": "string", "defaultValue": "", @@ -668,6 +708,13 @@ } }, "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)))))]" + } + ], "cmkUserAssignedIdentityAsArray": "[if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), createArray()))), createArray(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')), createArray())]", "userAssignedIdentitiesUnion": "[if(not(empty(parameters('managedIdentities'))), union(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), variables('cmkUserAssignedIdentityAsArray')), variables('cmkUserAssignedIdentityAsArray'))]", "formattedUserAssignedIdentities": "[reduce(map(coalesce(variables('userAssignedIdentitiesUnion'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", @@ -783,20 +830,20 @@ "workspace_roleAssignments": { "copy": { "name": "workspace_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Synapse/workspaces/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Synapse/workspaces', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Synapse/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "workspace" @@ -855,8 +902,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "12071965359188483950" + "version": "0.29.47.4906", + "templateHash": "4134219125418987684" }, "name": "Synapse Workspace Integration Runtimes", "description": "This module deploys a Synapse Workspace Integration Runtime.", @@ -953,8 +1000,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15762141115882405712" + "version": "0.29.47.4906", + "templateHash": "4045748488403006026" } }, "parameters": { @@ -1041,8 +1088,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "17207449952619386820" + "version": "0.29.47.4906", + "templateHash": "6170014210030770909" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key.", @@ -1149,8 +1196,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "351030244990389212" + "version": "0.29.47.4906", + "templateHash": "15890618337043813918" }, "name": "Synapse Workspaces Administrators", "description": "This module deploys Synapse Workspaces Administrators.", @@ -1231,6 +1278,112 @@ "workspace" ] }, + "workspace_firewallRules": { + "copy": { + "name": "workspace_firewallRules", + "count": "[length(coalesce(parameters('firewallRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-workspace-FirewallRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].name]" + }, + "endIpAddress": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].endIpAddress]" + }, + "startIpAddress": { + "value": "[coalesce(parameters('firewallRules'), createArray())[copyIndex()].startIpAddress]" + }, + "workspaceName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6729418057172891787" + }, + "name": "Synapse Workspaces Firewall Rules", + "description": "This module deploys Synapse Workspaces Firewall Rules.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Synapse Workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress." + } + } + }, + "resources": [ + { + "type": "Microsoft.Synapse/workspaces/firewallRules", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed firewall rule." + }, + "value": "[resourceId('Microsoft.Synapse/workspaces/firewallRules', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed firewall rule." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "workspace" + ] + }, "workspace_privateEndpoints": { "copy": { "name": "workspace_privateEndpoints", @@ -1295,8 +1448,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1308,6 +1461,13 @@ "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": { @@ -1411,13 +1571,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1451,8 +1611,11 @@ "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." + "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": { @@ -1492,8 +1655,11 @@ "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." + "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": { @@ -1643,6 +1809,13 @@ } }, "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')]", @@ -1660,8 +1833,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1679,7 +1852,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1720,20 +1893,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1766,8 +1939,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1813,7 +1986,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -1877,14 +2050,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } diff --git a/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep new file mode 100644 index 0000000000..11a476b1e7 --- /dev/null +++ b/avm/res/synapse/workspace/tests/e2e/fwrules/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + isHnsEnabled: true + } + + resource blobService 'blobServices@2022-09-01' = { + name: 'default' + + resource container 'containers@2022-09-01' = { + name: 'synapsews' + } + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The name of the created container.') +output storageContainerName string = storageAccount::blobService::container.name diff --git a/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep b/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep new file mode 100644 index 0000000000..4b5ea8d460 --- /dev/null +++ b/avm/res/synapse/workspace/tests/e2e/fwrules/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +metadata name = 'Using firewall rules' +metadata description = 'This instance deploys the module with the configuration of firewall rules.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-synapse.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'swfwr' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + storageAccountName: 'dep${namePrefix}sa${serviceShort}01' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + defaultDataLakeStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + defaultDataLakeStorageFilesystem: nestedDependencies.outputs.storageContainerName + sqlAdministratorLogin: 'synwsadmin' + firewallRules: [ + { + name: 'fwrule01' + endIpAddress: '87.14.134.20' + startIpAddress: '87.14.134.20' + } + { + name: 'fwrule02' + endIpAddress: '87.14.134.22' + startIpAddress: '87.14.134.21' + } + ] + } + } +] diff --git a/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep b/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep index ff749a63ba..f01bd99d5f 100644 --- a/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/synapse/workspace/tests/e2e/max/main.test.bicep @@ -85,11 +85,13 @@ module testDeployment '../../../main.bicep' = [ roleAssignments: [ { + name: '499f9243-2170-4204-807d-ee6d0f94a0d0' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/synapse/workspace/version.json b/avm/res/synapse/workspace/version.json index a8eda31021..e42c3d9e5f 100644 --- a/avm/res/synapse/workspace/version.json +++ b/avm/res/synapse/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/virtual-machine-images/image-template/README.md b/avm/res/virtual-machine-images/image-template/README.md index 9fa730d390..b63176a302 100644 --- a/avm/res/virtual-machine-images/image-template/README.md +++ b/avm/res/virtual-machine-images/image-template/README.md @@ -59,9 +59,9 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -312,9 +314,9 @@ module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -719,6 +723,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -769,6 +774,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/virtual-machine-images/image-template/main.bicep b/avm/res/virtual-machine-images/image-template/main.bicep index 2d218060f4..9aefe03417 100644 --- a/avm/res/virtual-machine-images/image-template/main.bicep +++ b/avm/res/virtual-machine-images/image-template/main.bicep @@ -88,6 +88,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.virtualmachineimages-imagetemplate.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -194,14 +205,10 @@ resource imageTemplate_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!em } resource imageTemplate_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(imageTemplate.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(imageTemplate.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -244,6 +251,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/virtual-machine-images/image-template/main.json b/avm/res/virtual-machine-images/image-template/main.json index ee1afba285..8af2add54f 100644 --- a/avm/res/virtual-machine-images/image-template/main.json +++ b/avm/res/virtual-machine-images/image-template/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11550984052424921620" + "version": "0.29.47.4906", + "templateHash": "12715542154180929791" }, "name": "Virtual Machine Image Templates", "description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).", @@ -43,6 +43,13 @@ "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": { @@ -536,6 +543,13 @@ } }, "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)))))]" + } + ], "identity": { "type": "UserAssigned", "userAssignedIdentities": "[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'))))]" @@ -615,20 +629,20 @@ "imageTemplate_roleAssignments": { "copy": { "name": "imageTemplate_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.VirtualMachineImages/imageTemplates/{0}', format('{0}-{1}', parameters('name'), parameters('baseTime')))]", - "name": "[guid(resourceId('Microsoft.VirtualMachineImages/imageTemplates', format('{0}-{1}', parameters('name'), parameters('baseTime'))), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.VirtualMachineImages/imageTemplates', format('{0}-{1}', parameters('name'), parameters('baseTime'))), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "imageTemplate" diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep index 9ead9906b8..bb557a787c 100644 --- a/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep +++ b/avm/res/virtual-machine-images/image-template/tests/e2e/defaults/main.test.bicep @@ -57,9 +57,9 @@ module testDeployment '../../../main.bicep' = { } ] imageSource: { - offer: 'Windows-10' + offer: 'Windows-11' publisher: 'MicrosoftWindowsDesktop' - sku: 'win10-22h2-ent' + sku: 'win11-23h2-ent' type: 'PlatformImage' version: 'latest' } diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep index 49eba449c6..ff20a8a137 100644 --- a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep +++ b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep @@ -98,8 +98,8 @@ module testDeployment '../../../main.bicep' = { imageSource: { type: 'PlatformImage' publisher: 'canonical' - offer: '0001-com-ubuntu-server-lunar' - sku: '23_04-gen2' + offer: 'ubuntu-24_04-lts' + sku: 'server' version: 'latest' } buildTimeoutInMinutes: 60 @@ -138,11 +138,13 @@ module testDeployment '../../../main.bicep' = { } roleAssignments: [ { + name: 'bb257a92-dc06-4831-9b74-ee5442d8ce0f' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/virtual-machine-images/image-template/version.json b/avm/res/virtual-machine-images/image-template/version.json index 9481fea58e..c177b1bb58 100644 --- a/avm/res/virtual-machine-images/image-template/version.json +++ b/avm/res/virtual-machine-images/image-template/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/web/connection/README.md b/avm/res/web/connection/README.md index 01c35c65c5..b0793eadd1 100644 --- a/avm/res/web/connection/README.md +++ b/avm/res/web/connection/README.md @@ -123,11 +123,13 @@ module connection 'br/public:avm/res/web/connection:' = { } roleAssignments: [ { + name: '396667c8-de54-4dcb-916a-72af71359f34' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -184,11 +186,13 @@ module connection 'br/public:avm/res/web/connection:' = { "roleAssignments": { "value": [ { + "name": "396667c8-de54-4dcb-916a-72af71359f34", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -474,6 +478,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -524,6 +529,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. diff --git a/avm/res/web/connection/main.bicep b/avm/res/web/connection/main.bicep index 2fc1070efa..caa5327bcf 100644 --- a/avm/res/web/connection/main.bicep +++ b/avm/res/web/connection/main.bicep @@ -113,6 +113,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + // ============== // // Resources // // ============== // @@ -165,14 +176,10 @@ resource connection_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty } resource connection_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(connection.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(connection.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -213,6 +220,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/web/connection/main.json b/avm/res/web/connection/main.json index 368726747d..e408288377 100644 --- a/avm/res/web/connection/main.json +++ b/avm/res/web/connection/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4760344619510029018" + "version": "0.29.47.4906", + "templateHash": "11729288021799571934" }, "name": "API Connections", "description": "This module deploys an Azure API Connection.", @@ -43,6 +43,13 @@ "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": { @@ -212,6 +219,13 @@ } }, "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')]", @@ -275,20 +289,20 @@ "connection_roleAssignments": { "copy": { "name": "connection_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Web/connections/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Web/connections', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/connections', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "connection" diff --git a/avm/res/web/connection/tests/e2e/max/main.test.bicep b/avm/res/web/connection/tests/e2e/max/main.test.bicep index a8ec604e38..57bfd52f89 100644 --- a/avm/res/web/connection/tests/e2e/max/main.test.bicep +++ b/avm/res/web/connection/tests/e2e/max/main.test.bicep @@ -62,11 +62,13 @@ module testDeployment '../../../main.bicep' = [ } roleAssignments: [ { + name: '396667c8-de54-4dcb-916a-72af71359f34' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/web/connection/version.json b/avm/res/web/connection/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/web/connection/version.json +++ b/avm/res/web/connection/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index 863ce72313..5d36f822dd 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -19,11 +19,12 @@ This module deploys a Web or Function App. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Web/sites` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites) | | `Microsoft.Web/sites/basicPublishingCredentialsPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | | `Microsoft.Web/sites/config` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | +| `Microsoft.Web/sites/extensions` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites/extensions) | | `Microsoft.Web/sites/hybridConnectionNamespaces/relays` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites/hybridConnectionNamespaces/relays) | | `Microsoft.Web/sites/slots` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites/slots) | | `Microsoft.Web/sites/slots/basicPublishingCredentialsPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | @@ -256,11 +257,13 @@ module site 'br/public:avm/res/web/site:' = { ] roleAssignments: [ { + name: '9efc9c10-f482-4af0-9acb-03b5a16f947e' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -459,11 +462,13 @@ module site 'br/public:avm/res/web/site:' = { "roleAssignments": { "value": [ { + "name": "9efc9c10-f482-4af0-9acb-03b5a16f947e", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -982,6 +987,7 @@ module site 'br/public:avm/res/web/site:' = { publicNetworkAccess: 'Disabled' roleAssignments: [ { + name: '0c2c82ef-069c-4085-b1bc-01614e0aa5ff' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' @@ -1051,11 +1057,13 @@ module site 'br/public:avm/res/web/site:' = { ] roleAssignments: [ { + name: '845ed19c-78e7-4422-aa3d-b78b67cd78a2' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -1207,6 +1215,7 @@ module site 'br/public:avm/res/web/site:' = { "roleAssignments": { "value": [ { + "name": "0c2c82ef-069c-4085-b1bc-01614e0aa5ff", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" @@ -1282,11 +1291,13 @@ module site 'br/public:avm/res/web/site:' = { ], "roleAssignments": [ { + "name": "845ed19c-78e7-4422-aa3d-b78b67cd78a2", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -2047,7 +2058,6 @@ module site 'br/public:avm/res/web/site:' = { | [`clientCertMode`](#parameter-clientcertmode) | string | This composes with ClientCertEnabled setting.

  • ClientCertEnabled=false means ClientCert is ignored.
  • ClientCertEnabled=true and ClientCertMode=Required means ClientCert is required.
  • ClientCertEnabled=true and ClientCertMode=Optional means ClientCert is optional or accepted.

    | | [`cloningInfo`](#parameter-cloninginfo) | object | If specified during app creation, the app is cloned from a source app. | | [`containerSize`](#parameter-containersize) | int | Size of the function container. | -| [`customDomainVerificationId`](#parameter-customdomainverificationid) | string | Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification. | | [`dailyMemoryTimeQuota`](#parameter-dailymemorytimequota) | int | Maximum allowed daily memory-time quota (applicable on dynamic apps only). | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`enabled`](#parameter-enabled) | bool | Setting this value to false disables the app (takes the app offline). | @@ -2060,7 +2070,9 @@ module site 'br/public:avm/res/web/site:' = { | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`logsConfiguration`](#parameter-logsconfiguration) | object | The logs settings configuration. | +| [`managedEnvironmentId`](#parameter-managedenvironmentid) | string | Azure Resource Manager ID of the customers selected Managed Environment on which to host this app. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`msDeployConfiguration`](#parameter-msdeployconfiguration) | object | The extension MSDeployment configuration. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | 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. | | [`redundancyMode`](#parameter-redundancymode) | string | Site redundancy mode. | @@ -2094,6 +2106,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' @@ -2209,13 +2222,6 @@ Size of the function container. - Required: No - Type: int -### Parameter: `customDomainVerificationId` - -Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification. - -- Required: No -- Type: string - ### Parameter: `dailyMemoryTimeQuota` Maximum allowed daily memory-time quota (applicable on dynamic apps only). @@ -2473,6 +2479,13 @@ The logs settings configuration. - Required: No - Type: object +### Parameter: `managedEnvironmentId` + +Azure Resource Manager ID of the customers selected Managed Environment on which to host this app. + +- Required: No +- Type: string + ### Parameter: `managedIdentities` The managed identity definition for this resource. @@ -2501,6 +2514,13 @@ The resource ID(s) to assign to the resource. - Required: No - Type: array +### Parameter: `msDeployConfiguration` + +The extension MSDeployment configuration. + +- Required: No +- Type: object + ### Parameter: `privateEndpoints` Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. @@ -2763,6 +2783,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -2813,6 +2834,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -2898,6 +2926,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -2948,6 +2977,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -3059,6 +3095,7 @@ Virtual Network Route All enabled. This causes all outbound traffic to have Virt | Output | Type | Description | | :-- | :-- | :-- | +| `customDomainVerificationId` | string | Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification. | | `defaultHostname` | string | Default hostname of the app. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the site. | @@ -3075,7 +3112,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Notes diff --git a/avm/res/web/site/basic-publishing-credentials-policy/main.json b/avm/res/web/site/basic-publishing-credentials-policy/main.json index 4a19891764..a2c95fcbcd 100644 --- a/avm/res/web/site/basic-publishing-credentials-policy/main.json +++ b/avm/res/web/site/basic-publishing-credentials-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7890454949460001265" + "version": "0.29.47.4906", + "templateHash": "5861139703409371797" }, "name": "Web Site Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Basic Publishing Credentials Policy.", diff --git a/avm/res/web/site/config--appsettings/README.md b/avm/res/web/site/config--appsettings/README.md index 4cf7e5f745..e62f230e72 100644 --- a/avm/res/web/site/config--appsettings/README.md +++ b/avm/res/web/site/config--appsettings/README.md @@ -57,6 +57,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' diff --git a/avm/res/web/site/config--appsettings/main.bicep b/avm/res/web/site/config--appsettings/main.bicep index cdb92d40e4..2f770a7774 100644 --- a/avm/res/web/site/config--appsettings/main.bicep +++ b/avm/res/web/site/config--appsettings/main.bicep @@ -12,6 +12,7 @@ param appName string 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app @@ -35,7 +36,7 @@ param appSettingsKeyValuePairs object? var azureWebJobsValues = !empty(storageAccountResourceId) && !(storageAccountUseIdentityAuthentication) ? { - AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};' + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' } : !empty(storageAccountResourceId) && storageAccountUseIdentityAuthentication ? union( diff --git a/avm/res/web/site/config--appsettings/main.json b/avm/res/web/site/config--appsettings/main.json index bacc559595..c59a554e2a 100644 --- a/avm/res/web/site/config--appsettings/main.json +++ b/avm/res/web/site/config--appsettings/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4955490965342210207" + "version": "0.29.47.4906", + "templateHash": "8777070640548664577" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -27,6 +27,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -97,7 +98,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "app", "appInsight", diff --git a/avm/res/web/site/config--authsettingsv2/README.md b/avm/res/web/site/config--authsettingsv2/README.md index 5894836b3f..ac501db552 100644 --- a/avm/res/web/site/config--authsettingsv2/README.md +++ b/avm/res/web/site/config--authsettingsv2/README.md @@ -55,6 +55,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' diff --git a/avm/res/web/site/config--authsettingsv2/main.bicep b/avm/res/web/site/config--authsettingsv2/main.bicep index 3466c630fd..d5e2466c41 100644 --- a/avm/res/web/site/config--authsettingsv2/main.bicep +++ b/avm/res/web/site/config--authsettingsv2/main.bicep @@ -12,6 +12,7 @@ param appName string 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app diff --git a/avm/res/web/site/config--authsettingsv2/main.json b/avm/res/web/site/config--authsettingsv2/main.json index 3f09f15ef6..88d23811d2 100644 --- a/avm/res/web/site/config--authsettingsv2/main.json +++ b/avm/res/web/site/config--authsettingsv2/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8625962477180026020" + "version": "0.29.47.4906", + "templateHash": "15126303852151434516" }, "name": "Site Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -26,6 +26,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", diff --git a/avm/res/web/site/config--logs/main.json b/avm/res/web/site/config--logs/main.json index 10f2eaecbb..a26a2fc11e 100644 --- a/avm/res/web/site/config--logs/main.json +++ b/avm/res/web/site/config--logs/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10404897918297908598" + "version": "0.29.47.4906", + "templateHash": "1719886395722436280" }, "name": "Site logs Config", "description": "This module deploys a Site logs Configuration.", diff --git a/avm/res/web/site/config--web/main.json b/avm/res/web/site/config--web/main.json index afe89e4c2e..3a995656e4 100644 --- a/avm/res/web/site/config--web/main.json +++ b/avm/res/web/site/config--web/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7729394396836682981" + "version": "0.29.47.4906", + "templateHash": "7195763436259190781" }, "name": "Site Api Management Config", "description": "This module deploys a Site Api Management Configuration.", diff --git a/avm/res/web/site/extensions--msdeploy/README.md b/avm/res/web/site/extensions--msdeploy/README.md new file mode 100644 index 0000000000..9d563b7732 --- /dev/null +++ b/avm/res/web/site/extensions--msdeploy/README.md @@ -0,0 +1,57 @@ +# Site Deployment Extension `[Microsoft.Web/sites/extensions]` + +This module deploys a Site extension for MSDeploy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Web/sites/extensions` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites/extensions) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`appName`](#parameter-appname) | string | The name of the parent site resource. | +| [`msDeployConfiguration`](#parameter-msdeployconfiguration) | object | Sets the MSDeployment Properties. | + +### Parameter: `appName` + +The name of the parent site resource. + +- Required: Yes +- Type: string + +### Parameter: `msDeployConfiguration` + +Sets the MSDeployment Properties. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the MSDeploy Package. | +| `resourceGroupName` | string | The resource group the site config was deployed into. | +| `resourceId` | string | The resource ID of the Site Extension. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/web/site/extensions--msdeploy/main.bicep b/avm/res/web/site/extensions--msdeploy/main.bicep new file mode 100644 index 0000000000..07d8ef3405 --- /dev/null +++ b/avm/res/web/site/extensions--msdeploy/main.bicep @@ -0,0 +1,28 @@ +metadata name = 'Site Deployment Extension ' +metadata description = 'This module deploys a Site extension for MSDeploy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the parent site resource.') +param appName string + +@description('Required. Sets the MSDeployment Properties.') +param msDeployConfiguration object? + +resource app 'Microsoft.Web/sites@2022-09-01' existing = { + name: appName +} +resource msdeploy 'Microsoft.Web/sites/extensions@2023-12-01' = { + name: 'MSDeploy' + kind: 'MSDeploy' + parent: app + properties: msDeployConfiguration +} + +@description('The name of the MSDeploy Package.') +output name string = msdeploy.name + +@description('The resource ID of the Site Extension.') +output resourceId string = msdeploy.id + +@description('The resource group the site config was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/web/site/extensions--msdeploy/main.json b/avm/res/web/site/extensions--msdeploy/main.json new file mode 100644 index 0000000000..fdc1b30f96 --- /dev/null +++ b/avm/res/web/site/extensions--msdeploy/main.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2006653133597094766" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Required. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2022-09-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2023-12-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]", + "dependsOn": [ + "app" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/web/site/hybrid-connection-namespace/relay/main.json b/avm/res/web/site/hybrid-connection-namespace/relay/main.json index c213584793..c0dd469939 100644 --- a/avm/res/web/site/hybrid-connection-namespace/relay/main.json +++ b/avm/res/web/site/hybrid-connection-namespace/relay/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3173546331275321765" + "version": "0.29.47.4906", + "templateHash": "7661794789768148013" }, "name": "Web/Function Apps Hybrid Connection Relay", "description": "This module deploys a Site Hybrid Connection Namespace Relay.", diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 045a03121b..94bb8d7f36 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -15,6 +15,7 @@ param location string = resourceGroup().location 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app @@ -27,6 +28,9 @@ param kind string @description('Required. The resource ID of the app service plan to use for the site.') param serverFarmResourceId string +@description('Optional. Azure Resource Manager ID of the customers selected Managed Environment on which to host this app.') +param managedEnvironmentId string? + @description('Optional. Configures a site to accept only HTTPS requests. Issues redirect for HTTP requests.') param httpsOnly bool = true @@ -74,6 +78,9 @@ param storageAccountUseIdentityAuthentication bool = false @description('Optional. The web settings api management configuration.') param apiManagementConfiguration object? +@description('Optional. The extension MSDeployment configuration.') +param msDeployConfiguration object? + @description('Optional. Resource ID of the app insight to leverage for this resource.') param appInsightResourceId string? @@ -132,9 +139,6 @@ param cloningInfo object? @description('Optional. Size of the function container.') param containerSize int? -@description('Optional. Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification.') -param customDomainVerificationId string? - @description('Optional. Maximum allowed daily memory-time quota (applicable on dynamic apps only).') param dailyMemoryTimeQuota int? @@ -211,6 +215,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.web-site.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -237,6 +252,7 @@ resource app 'Microsoft.Web/sites@2022-09-01' = { tags: tags identity: identity properties: { + managedEnvironmentId: !empty(managedEnvironmentId) ? managedEnvironmentId : null serverFarmId: serverFarmResourceId clientAffinityEnabled: clientAffinityEnabled httpsOnly: httpsOnly @@ -254,7 +270,6 @@ resource app 'Microsoft.Web/sites@2022-09-01' = { clientCertMode: clientCertMode cloningInfo: cloningInfo containerSize: containerSize - customDomainVerificationId: customDomainVerificationId dailyMemoryTimeQuota: dailyMemoryTimeQuota enabled: enabled hostNameSslStates: hostNameSslStates @@ -270,7 +285,7 @@ resource app 'Microsoft.Web/sites@2022-09-01' = { } } -module app_appsettings 'config--appsettings/main.bicep' = if (!empty(appSettingsKeyValuePairs)) { +module app_appsettings 'config--appsettings/main.bicep' = if (!empty(appSettingsKeyValuePairs) || !empty(appInsightResourceId) || !empty(storageAccountResourceId)) { name: '${uniqueString(deployment().name, location)}-Site-Config-AppSettings' params: { appName: app.name @@ -310,6 +325,14 @@ module app_websettings 'config--web/main.bicep' = if (!empty(apiManagementConfig } } +module extension_msdeploy 'extensions--msdeploy/main.bicep' = if (!empty(msDeployConfiguration)) { + name: '${uniqueString(deployment().name, location)}-Site-Extension-MSDeploy' + params: { + appName: app.name + msDeployConfiguration: msDeployConfiguration ?? {} + } +} + @batchSize(1) module app_slots 'slot/main.bicep' = [ for (slot, index) in (slots ?? []): { @@ -332,8 +355,9 @@ module app_slots 'slot/main.bicep' = [ storageAccountUseIdentityAuthentication: slot.?storageAccountUseIdentityAuthentication ?? storageAccountUseIdentityAuthentication appInsightResourceId: slot.?appInsightResourceId ?? appInsightResourceId authSettingV2Configuration: slot.?authSettingV2Configuration ?? authSettingV2Configuration + msDeployConfiguration: slot.?msDeployConfiguration ?? msDeployConfiguration diagnosticSettings: slot.?diagnosticSettings - roleAssignments: slot.?roleAssignments ?? roleAssignments + roleAssignments: slot.?roleAssignments appSettingsKeyValuePairs: slot.?appSettingsKeyValuePairs ?? appSettingsKeyValuePairs basicPublishingCredentialsPolicies: slot.?basicPublishingCredentialsPolicies ?? basicPublishingCredentialsPolicies lock: slot.?lock ?? lock @@ -426,14 +450,10 @@ resource app_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-0 ] resource app_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(app.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(app.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -445,7 +465,7 @@ resource app_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01 } ] -module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-app-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -527,6 +547,9 @@ output location string = app.location @description('Default hostname of the app.') output defaultHostname string = app.properties.defaultHostName +@description('Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification.') +output customDomainVerificationId string = app.properties.customDomainVerificationId + // =============== // // Definitions // // =============== // @@ -548,6 +571,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 5f97b86fb3..f32bf97d77 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "984038015653934080" + "version": "0.29.47.4906", + "templateHash": "7467350205860855506" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -66,6 +66,13 @@ "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": { @@ -467,6 +474,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -484,6 +492,13 @@ "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, @@ -590,6 +605,13 @@ "description": "Optional. The web settings api management configuration." } }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The extension MSDeployment configuration." + } + }, "appInsightResourceId": { "type": "string", "nullable": true, @@ -703,13 +725,6 @@ "description": "Optional. Size of the function container." } }, - "customDomainVerificationId": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. Unique identifier that verifies the custom domains assigned to the app. Customer will add this ID to a txt record for verification." - } - }, "dailyMemoryTimeQuota": { "type": "int", "nullable": true, @@ -779,6 +794,13 @@ } }, "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": { @@ -822,6 +844,7 @@ "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')]", @@ -835,7 +858,6 @@ "clientCertMode": "[parameters('clientCertMode')]", "cloningInfo": "[parameters('cloningInfo')]", "containerSize": "[parameters('containerSize')]", - "customDomainVerificationId": "[parameters('customDomainVerificationId')]", "dailyMemoryTimeQuota": "[parameters('dailyMemoryTimeQuota')]", "enabled": "[parameters('enabled')]", "hostNameSslStates": "[parameters('hostNameSslStates')]", @@ -906,27 +928,27 @@ "app_roleAssignments": { "copy": { "name": "app_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Web/sites/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Web/sites', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/sites', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "app" ] }, "app_appsettings": { - "condition": "[not(empty(parameters('appSettingsKeyValuePairs')))]", + "condition": "[or(or(not(empty(parameters('appSettingsKeyValuePairs'))), not(empty(parameters('appInsightResourceId')))), not(empty(parameters('storageAccountResourceId'))))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-Site-Config-AppSettings', uniqueString(deployment().name, parameters('location')))]", @@ -962,8 +984,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "1375917194796674785" + "version": "0.29.47.4906", + "templateHash": "8777070640548664577" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -984,6 +1006,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -1054,7 +1077,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "app", "appInsight", @@ -1118,8 +1141,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "14800328886021060025" + "version": "0.29.47.4906", + "templateHash": "15126303852151434516" }, "name": "Site Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -1140,6 +1163,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -1221,8 +1245,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "10404897918297908598" + "version": "0.29.47.4906", + "templateHash": "1719886395722436280" }, "name": "Site logs Config", "description": "This module deploys a Site logs Configuration.", @@ -1316,8 +1340,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7729394396836682981" + "version": "0.29.47.4906", + "templateHash": "7195763436259190781" }, "name": "Site Api Management Config", "description": "This module deploys a Site Api Management Configuration.", @@ -1385,6 +1409,100 @@ "app" ] }, + "extension_msdeploy": { + "condition": "[not(empty(parameters('msDeployConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Extension-MSDeploy', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('name')]" + }, + "msDeployConfiguration": { + "value": "[coalesce(parameters('msDeployConfiguration'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2006653133597094766" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Required. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2022-09-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2023-12-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]", + "dependsOn": [ + "app" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "app" + ] + }, "app_slots": { "copy": { "name": "app_slots", @@ -1452,11 +1570,14 @@ "authSettingV2Configuration": { "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'authSettingV2Configuration'), parameters('authSettingV2Configuration'))]" }, + "msDeployConfiguration": { + "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'msDeployConfiguration'), parameters('msDeployConfiguration'))]" + }, "diagnosticSettings": { "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'diagnosticSettings')]" }, "roleAssignments": { - "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'roleAssignments'), parameters('roleAssignments'))]" + "value": "[tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'roleAssignments')]" }, "appSettingsKeyValuePairs": { "value": "[coalesce(tryGet(coalesce(parameters('slots'), createArray())[copyIndex()], 'appSettingsKeyValuePairs'), parameters('appSettingsKeyValuePairs'))]" @@ -1532,8 +1653,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17910920737432101876" + "version": "0.29.47.4906", + "templateHash": "8134670421794628910" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -1593,6 +1714,13 @@ "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": { @@ -2000,6 +2128,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -2110,6 +2239,13 @@ "description": "Optional. The auth settings V2 configuration." } }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The extension MSDeployment configuration." + } + }, "lock": { "$ref": "#/definitions/lockType", "metadata": { @@ -2285,6 +2421,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -2400,20 +2543,20 @@ "slot_roleAssignments": { "copy": { "name": "slot_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Web/sites/{0}/slots/{1}', parameters('appName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "slot" @@ -2459,8 +2602,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "12787561964805698777" + "version": "0.29.47.4906", + "templateHash": "7111332561212908044" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -2487,6 +2630,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -2566,7 +2710,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", @@ -2634,8 +2778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "17648822117505176946" + "version": "0.29.47.4906", + "templateHash": "3412962465179136371" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -2662,6 +2806,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -2755,8 +2900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "13815624464535684714" + "version": "0.29.47.4906", + "templateHash": "6875784212879192632" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy.", @@ -2881,8 +3026,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "18259551022155592905" + "version": "0.29.47.4906", + "templateHash": "4120073340411344208" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay.", @@ -2962,6 +3107,100 @@ "slot" ] }, + "slot_extensionMSdeploy": { + "condition": "[not(empty(parameters('msDeployConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Extension-MSDeploy', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('appName')]" + }, + "msDeployConfiguration": { + "value": "[coalesce(parameters('msDeployConfiguration'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2006653133597094766" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Required. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2022-09-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2023-12-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]", + "dependsOn": [ + "app" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "app" + ] + }, "slot_privateEndpoints": { "copy": { "name": "slot_privateEndpoints", @@ -3026,8 +3265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -3039,6 +3278,13 @@ "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": { @@ -3142,13 +3388,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -3182,8 +3428,11 @@ "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." + "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": { @@ -3223,8 +3472,11 @@ "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." + "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": { @@ -3374,6 +3626,13 @@ } }, "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')]", @@ -3391,8 +3650,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -3410,7 +3669,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -3451,20 +3710,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -3497,8 +3756,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -3544,7 +3803,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -3608,14 +3867,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -3702,8 +3968,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6464393553547779506" + "version": "0.29.47.4906", + "templateHash": "5861139703409371797" }, "name": "Web Site Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Basic Publishing Credentials Policy.", @@ -3818,8 +4084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "6008462668933940185" + "version": "0.29.47.4906", + "templateHash": "7661794789768148013" }, "name": "Web/Function Apps Hybrid Connection Relay", "description": "This module deploys a Site Hybrid Connection Namespace Relay.", @@ -3956,8 +4222,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -3969,6 +4235,13 @@ "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": { @@ -4072,13 +4345,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -4112,8 +4385,11 @@ "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." + "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": { @@ -4153,8 +4429,11 @@ "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." + "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": { @@ -4304,6 +4583,13 @@ } }, "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')]", @@ -4321,8 +4607,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -4340,7 +4626,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -4381,20 +4667,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -4427,8 +4713,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -4474,7 +4760,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -4538,14 +4824,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -4627,6 +4920,13 @@ "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]" } } -} \ No newline at end of file +} diff --git a/avm/res/web/site/slot/README.md b/avm/res/web/site/slot/README.md index 14d481c0e1..a283ad6bef 100644 --- a/avm/res/web/site/slot/README.md +++ b/avm/res/web/site/slot/README.md @@ -18,8 +18,9 @@ This module deploys a Web or Function App Deployment Slot. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Web/sites/extensions` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites/extensions) | | `Microsoft.Web/sites/slots` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites/slots) | | `Microsoft.Web/sites/slots/basicPublishingCredentialsPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | | `Microsoft.Web/sites/slots/config` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | @@ -68,6 +69,7 @@ This module deploys a Web or Function App Deployment Slot. | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`msDeployConfiguration`](#parameter-msdeployconfiguration) | object | The extension MSDeployment configuration. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. | | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Allow or block all public traffic. | | [`redundancyMode`](#parameter-redundancymode) | string | Site redundancy mode. | @@ -100,6 +102,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' @@ -493,6 +496,13 @@ The resource ID(s) to assign to the resource. - Required: No - Type: array +### Parameter: `msDeployConfiguration` + +The extension MSDeployment configuration. + +- Required: No +- Type: object + ### Parameter: `privateEndpoints` Configuration details for private endpoints. @@ -755,6 +765,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -805,6 +816,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -890,6 +908,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -940,6 +959,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1055,7 +1081,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Notes diff --git a/avm/res/web/site/slot/basic-publishing-credentials-policy/main.json b/avm/res/web/site/slot/basic-publishing-credentials-policy/main.json index 1abd91e231..93bbb33ac2 100644 --- a/avm/res/web/site/slot/basic-publishing-credentials-policy/main.json +++ b/avm/res/web/site/slot/basic-publishing-credentials-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16535586328125636135" + "version": "0.29.47.4906", + "templateHash": "6875784212879192632" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy.", diff --git a/avm/res/web/site/slot/config--appsettings/README.md b/avm/res/web/site/slot/config--appsettings/README.md index 50d74670f4..8752b04ab3 100644 --- a/avm/res/web/site/slot/config--appsettings/README.md +++ b/avm/res/web/site/slot/config--appsettings/README.md @@ -58,6 +58,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' diff --git a/avm/res/web/site/slot/config--appsettings/main.bicep b/avm/res/web/site/slot/config--appsettings/main.bicep index 9b65f79a1e..74095161a8 100644 --- a/avm/res/web/site/slot/config--appsettings/main.bicep +++ b/avm/res/web/site/slot/config--appsettings/main.bicep @@ -15,6 +15,7 @@ param appName string 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app @@ -38,7 +39,7 @@ param appSettingsKeyValuePairs object? var azureWebJobsValues = !empty(storageAccountResourceId) && !(storageAccountUseIdentityAuthentication) ? { - AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};' + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' } : !empty(storageAccountResourceId) && storageAccountUseIdentityAuthentication ? union( diff --git a/avm/res/web/site/slot/config--appsettings/main.json b/avm/res/web/site/slot/config--appsettings/main.json index be785047a0..48ed22304b 100644 --- a/avm/res/web/site/slot/config--appsettings/main.json +++ b/avm/res/web/site/slot/config--appsettings/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7468244783040054160" + "version": "0.29.47.4906", + "templateHash": "7111332561212908044" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -33,6 +33,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -112,7 +113,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", diff --git a/avm/res/web/site/slot/config--authsettingsv2/README.md b/avm/res/web/site/slot/config--authsettingsv2/README.md index d0f59777ba..16cb5fdc98 100644 --- a/avm/res/web/site/slot/config--authsettingsv2/README.md +++ b/avm/res/web/site/slot/config--authsettingsv2/README.md @@ -56,6 +56,7 @@ Type of site to deploy. 'functionapp' 'functionapp,linux' 'functionapp,linux,container' + 'functionapp,linux,container,azurecontainerapps' 'functionapp,workflowapp' 'functionapp,workflowapp,linux' 'linux,api' diff --git a/avm/res/web/site/slot/config--authsettingsv2/main.bicep b/avm/res/web/site/slot/config--authsettingsv2/main.bicep index 434eb52556..7b6b50f0a4 100644 --- a/avm/res/web/site/slot/config--authsettingsv2/main.bicep +++ b/avm/res/web/site/slot/config--authsettingsv2/main.bicep @@ -15,6 +15,7 @@ param slotName string 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app diff --git a/avm/res/web/site/slot/config--authsettingsv2/main.json b/avm/res/web/site/slot/config--authsettingsv2/main.json index f8120232a0..489aa559b3 100644 --- a/avm/res/web/site/slot/config--authsettingsv2/main.json +++ b/avm/res/web/site/slot/config--authsettingsv2/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10212981101211605812" + "version": "0.29.47.4906", + "templateHash": "3412962465179136371" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -32,6 +32,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", diff --git a/avm/res/web/site/slot/extensions--msdeploy/README.md b/avm/res/web/site/slot/extensions--msdeploy/README.md new file mode 100644 index 0000000000..ecb367dce1 --- /dev/null +++ b/avm/res/web/site/slot/extensions--msdeploy/README.md @@ -0,0 +1,57 @@ +# Site Deployment Extension `[Microsoft.Web/sites/slots/extensions]` + +This module deploys a Site extension for MSDeploy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Web/sites/extensions` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites/extensions) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`appName`](#parameter-appname) | string | The name of the parent site resource. | +| [`msDeployConfiguration`](#parameter-msdeployconfiguration) | object | Sets the MSDeployment Properties. | + +### Parameter: `appName` + +The name of the parent site resource. + +- Required: Yes +- Type: string + +### Parameter: `msDeployConfiguration` + +Sets the MSDeployment Properties. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the MSDeploy Package. | +| `resourceGroupName` | string | The resource group the site config was deployed into. | +| `resourceId` | string | The resource ID of the Site Extension. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/web/site/slot/extensions--msdeploy/main.bicep b/avm/res/web/site/slot/extensions--msdeploy/main.bicep new file mode 100644 index 0000000000..07d8ef3405 --- /dev/null +++ b/avm/res/web/site/slot/extensions--msdeploy/main.bicep @@ -0,0 +1,28 @@ +metadata name = 'Site Deployment Extension ' +metadata description = 'This module deploys a Site extension for MSDeploy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the parent site resource.') +param appName string + +@description('Required. Sets the MSDeployment Properties.') +param msDeployConfiguration object? + +resource app 'Microsoft.Web/sites@2022-09-01' existing = { + name: appName +} +resource msdeploy 'Microsoft.Web/sites/extensions@2023-12-01' = { + name: 'MSDeploy' + kind: 'MSDeploy' + parent: app + properties: msDeployConfiguration +} + +@description('The name of the MSDeploy Package.') +output name string = msdeploy.name + +@description('The resource ID of the Site Extension.') +output resourceId string = msdeploy.id + +@description('The resource group the site config was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/web/site/slot/extensions--msdeploy/main.json b/avm/res/web/site/slot/extensions--msdeploy/main.json new file mode 100644 index 0000000000..fdc1b30f96 --- /dev/null +++ b/avm/res/web/site/slot/extensions--msdeploy/main.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2006653133597094766" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Required. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2022-09-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2023-12-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]", + "dependsOn": [ + "app" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/web/site/slot/hybrid-connection-namespace/relay/main.json b/avm/res/web/site/slot/hybrid-connection-namespace/relay/main.json index 8e35a12e77..fb8ad95a8d 100644 --- a/avm/res/web/site/slot/hybrid-connection-namespace/relay/main.json +++ b/avm/res/web/site/slot/hybrid-connection-namespace/relay/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15454748136055010211" + "version": "0.29.47.4906", + "templateHash": "4120073340411344208" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay.", diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index 5ac80ec90a..75b5dfb233 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -18,6 +18,7 @@ param location string = resourceGroup().location 'functionapp,workflowapp' // logic app workflow 'functionapp,workflowapp,linux' // logic app docker container 'functionapp,linux,container' // function app linux container + 'functionapp,linux,container,azurecontainerapps' // function app linux container azure container apps 'app,linux' // linux web app 'app' // windows web app 'linux,api' // linux api app @@ -71,6 +72,9 @@ param appSettingsKeyValuePairs object? @description('Optional. The auth settings V2 configuration.') param authSettingV2Configuration object? +@description('Optional. The extension MSDeployment configuration.') +param msDeployConfiguration object? + @description('Optional. The lock settings of the service.') param lock lockType @@ -197,6 +201,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + resource app 'Microsoft.Web/sites@2021-03-01' existing = { name: appName } @@ -286,6 +301,14 @@ module slot_hybridConnectionRelays 'hybrid-connection-namespace/relay/main.bicep } ] +module slot_extensionMSdeploy 'extensions--msdeploy/main.bicep' = if (!empty(msDeployConfiguration)) { + name: '${uniqueString(deployment().name, location)}-Site-Extension-MSDeploy' + params: { + appName: app.name + msDeployConfiguration: msDeployConfiguration ?? {} + } +} + resource slot_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { name: lock.?name ?? 'lock-${name}' properties: { @@ -327,14 +350,10 @@ resource slot_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05- ] resource slot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(slot.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(slot.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -346,7 +365,7 @@ resource slot_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-0 } ] -module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module slot_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-slot-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -435,6 +454,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index 335acafe34..f9a5b7f974 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15715280445379891193" + "version": "0.29.47.4906", + "templateHash": "8134670421794628910" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -66,6 +66,13 @@ "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": { @@ -473,6 +480,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -583,6 +591,13 @@ "description": "Optional. The auth settings V2 configuration." } }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The extension MSDeployment configuration." + } + }, "lock": { "$ref": "#/definitions/lockType", "metadata": { @@ -758,6 +773,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -873,20 +895,20 @@ "slot_roleAssignments": { "copy": { "name": "slot_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Web/sites/{0}/slots/{1}', parameters('appName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "slot" @@ -932,8 +954,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7468244783040054160" + "version": "0.29.47.4906", + "templateHash": "7111332561212908044" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -960,6 +982,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -1039,7 +1062,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", @@ -1107,8 +1130,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10212981101211605812" + "version": "0.29.47.4906", + "templateHash": "3412962465179136371" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -1135,6 +1158,7 @@ "functionapp,workflowapp", "functionapp,workflowapp,linux", "functionapp,linux,container", + "functionapp,linux,container,azurecontainerapps", "app,linux", "app", "linux,api", @@ -1228,8 +1252,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16535586328125636135" + "version": "0.29.47.4906", + "templateHash": "6875784212879192632" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy.", @@ -1354,8 +1378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15454748136055010211" + "version": "0.29.47.4906", + "templateHash": "4120073340411344208" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay.", @@ -1435,6 +1459,100 @@ "slot" ] }, + "slot_extensionMSdeploy": { + "condition": "[not(empty(parameters('msDeployConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Site-Extension-MSDeploy', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appName": { + "value": "[parameters('appName')]" + }, + "msDeployConfiguration": { + "value": "[coalesce(parameters('msDeployConfiguration'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2006653133597094766" + }, + "name": "Site Deployment Extension ", + "description": "This module deploys a Site extension for MSDeploy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent site resource." + } + }, + "msDeployConfiguration": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Required. Sets the MSDeployment Properties." + } + } + }, + "resources": { + "app": { + "existing": true, + "type": "Microsoft.Web/sites", + "apiVersion": "2022-09-01", + "name": "[parameters('appName')]" + }, + "msdeploy": { + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2023-12-01", + "name": "[format('{0}/{1}', parameters('appName'), 'MSDeploy')]", + "kind": "MSDeploy", + "properties": "[parameters('msDeployConfiguration')]", + "dependsOn": [ + "app" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the MSDeploy Package." + }, + "value": "MSDeploy" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Site Extension." + }, + "value": "[resourceId('Microsoft.Web/sites/extensions', parameters('appName'), 'MSDeploy')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the site config was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "app" + ] + }, "slot_privateEndpoints": { "copy": { "name": "slot_privateEndpoints", @@ -1499,8 +1617,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1512,6 +1630,13 @@ "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": { @@ -1615,13 +1740,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1655,8 +1780,11 @@ "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." + "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": { @@ -1696,8 +1824,11 @@ "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." + "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": { @@ -1847,6 +1978,13 @@ } }, "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')]", @@ -1864,8 +2002,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1883,7 +2021,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1924,20 +2062,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1970,8 +2108,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -2017,7 +2155,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -2081,14 +2219,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } @@ -2136,4 +2281,4 @@ "value": "[reference('slot', '2022-09-01', 'full').location]" } } -} \ No newline at end of file +} diff --git a/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep b/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep index 4ec82011d0..6e006101c9 100644 --- a/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep +++ b/avm/res/web/site/tests/e2e/functionApp.max/main.test.bicep @@ -192,11 +192,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '9efc9c10-f482-4af0-9acb-03b5a16f947e' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep b/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep index 434166b1e2..c11fda0625 100644 --- a/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep +++ b/avm/res/web/site/tests/e2e/webApp.max/main.test.bicep @@ -129,11 +129,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '845ed19c-78e7-4422-aa3d-b78b67cd78a2' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' @@ -200,6 +202,7 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: '0c2c82ef-069c-4085-b1bc-01614e0aa5ff' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/web/site/version.json b/avm/res/web/site/version.json index c177b1bb58..9ed3662aba 100644 --- a/avm/res/web/site/version.json +++ b/avm/res/web/site/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} diff --git a/avm/res/web/static-site/README.md b/avm/res/web/static-site/README.md index 8e078430bd..50ec527b3b 100644 --- a/avm/res/web/static-site/README.md +++ b/avm/res/web/static-site/README.md @@ -17,8 +17,8 @@ This module deploys a Static Web App. | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Web/staticSites` | [2021-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2021-03-01/staticSites) | | `Microsoft.Web/staticSites/config` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/staticSites/config) | | `Microsoft.Web/staticSites/customDomains` | [2022-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-03-01/staticSites/customDomains) | @@ -145,11 +145,13 @@ module staticSite 'br/public:avm/res/web/static-site:' = { ] roleAssignments: [ { + name: 'ba1328f0-c7ab-47bf-afbf-0637b9c02bbe' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Owner' } { + name: '' principalId: '' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' @@ -252,11 +254,13 @@ module staticSite 'br/public:avm/res/web/static-site:' = { "roleAssignments": { "value": [ { + "name": "ba1328f0-c7ab-47bf-afbf-0637b9c02bbe", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Owner" }, { + "name": "", "principalId": "", "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" @@ -877,6 +881,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `privateEndpoints.roleAssignments.principalId` @@ -927,6 +932,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1002,6 +1014,7 @@ Array of role assignments to create. | [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | | [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | | [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | | [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | ### Parameter: `roleAssignments.principalId` @@ -1052,6 +1065,13 @@ The description of the role assignment. - Required: No - Type: string +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + ### Parameter: `roleAssignments.principalType` The principal type of the assigned principal ID. @@ -1131,7 +1151,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | +| `br/public:avm/res/network/private-endpoint:0.6.1` | Remote reference | ## Data Collection diff --git a/avm/res/web/static-site/config/main.json b/avm/res/web/static-site/config/main.json index 96a2b0cc5b..a0c035686f 100644 --- a/avm/res/web/static-site/config/main.json +++ b/avm/res/web/static-site/config/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2343320964001139856" + "version": "0.29.47.4906", + "templateHash": "6259766574135261184" }, "name": "Static Web App Site Config", "description": "This module deploys a Static Web App Site Config.", diff --git a/avm/res/web/static-site/custom-domain/main.json b/avm/res/web/static-site/custom-domain/main.json index 46ecfd284c..f6b82f3bb8 100644 --- a/avm/res/web/static-site/custom-domain/main.json +++ b/avm/res/web/static-site/custom-domain/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3789549854358718812" + "version": "0.29.47.4906", + "templateHash": "4543869723891386084" }, "name": "Static Web App Site Custom Domains", "description": "This module deploys a Static Web App Site Custom Domain.", diff --git a/avm/res/web/static-site/linked-backend/main.json b/avm/res/web/static-site/linked-backend/main.json index cc44a2455b..b5446cc820 100644 --- a/avm/res/web/static-site/linked-backend/main.json +++ b/avm/res/web/static-site/linked-backend/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7945079053497234244" + "version": "0.29.47.4906", + "templateHash": "12808204824131403769" }, "name": "Static Web App Site Linked Backends", "description": "This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.", diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep index 0881a96960..aeaf56fe22 100644 --- a/avm/res/web/static-site/main.bicep +++ b/avm/res/web/static-site/main.bicep @@ -122,6 +122,17 @@ var builtInRoleNames = { ) } +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)) + }) +] + #disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { name: '46d3xbcp.res.web-staticSite.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' @@ -215,14 +226,10 @@ resource staticSite_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty } resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ - for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(staticSite.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(staticSite.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: roleAssignment.roleDefinitionId principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType @@ -234,7 +241,7 @@ resource staticSite_roleAssignments 'Microsoft.Authorization/roleAssignments@202 } ] -module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ +module staticSite_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.6.1' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-staticSite-PrivateEndpoint-${index}' scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') @@ -326,6 +333,9 @@ type lockType = { }? type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') roleDefinitionIdOrName: string diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json index 43f5f0d015..7317946c9c 100644 --- a/avm/res/web/static-site/main.json +++ b/avm/res/web/static-site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9983026640972925367" + "version": "0.29.47.4906", + "templateHash": "13375787239295645610" }, "name": "Static Web Apps", "description": "This module deploys a Static Web App.", @@ -66,6 +66,13 @@ "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": { @@ -486,6 +493,13 @@ } }, "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', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -559,20 +573,20 @@ "staticSite_roleAssignments": { "copy": { "name": "staticSite_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Web/staticSites/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Web/staticSites', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Web/staticSites', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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": [ "staticSite" @@ -603,8 +617,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7945079053497234244" + "version": "0.29.47.4906", + "templateHash": "12808204824131403769" }, "name": "Static Web App Site Linked Backends", "description": "This module deploys a Custom Function App into a Static Web App Site using the Linked Backends property.", @@ -705,8 +719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2343320964001139856" + "version": "0.29.47.4906", + "templateHash": "6259766574135261184" }, "name": "Static Web App Site Config", "description": "This module deploys a Static Web App Site Config.", @@ -800,8 +814,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2343320964001139856" + "version": "0.29.47.4906", + "templateHash": "6259766574135261184" }, "name": "Static Web App Site Config", "description": "This module deploys a Static Web App Site Config.", @@ -896,8 +910,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3789549854358718812" + "version": "0.29.47.4906", + "templateHash": "4543869723891386084" }, "name": "Static Web App Site Custom Domains", "description": "This module deploys a Static Web App Site Custom Domain.", @@ -1027,8 +1041,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4120048060064073955" + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -1040,6 +1054,13 @@ "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": { @@ -1143,13 +1164,13 @@ "groupId": { "type": "string", "metadata": { - "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + "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." + "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": { @@ -1183,8 +1204,11 @@ "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." + "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": { @@ -1224,8 +1248,11 @@ "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." + "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": { @@ -1375,6 +1402,13 @@ } }, "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')]", @@ -1392,8 +1426,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1411,7 +1445,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1452,20 +1486,20 @@ "privateEndpoint_roleAssignments": { "copy": { "name": "privateEndpoint_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "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": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "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" @@ -1498,8 +1532,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11244630631275470040" + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group.", @@ -1545,7 +1579,7 @@ "resources": [ { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-04-01", + "apiVersion": "2023-11-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" @@ -1609,14 +1643,21 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" }, "groupId": { "type": "string", "metadata": { "description": "The group Id for the private endpoint Group." }, - "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + "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), ''), ''))]" } } } diff --git a/avm/res/web/static-site/tests/e2e/max/main.test.bicep b/avm/res/web/static-site/tests/e2e/max/main.test.bicep index 45de7cf720..0e3371b513 100644 --- a/avm/res/web/static-site/tests/e2e/max/main.test.bicep +++ b/avm/res/web/static-site/tests/e2e/max/main.test.bicep @@ -82,11 +82,13 @@ module testDeployment '../../../main.bicep' = [ ] roleAssignments: [ { + name: 'ba1328f0-c7ab-47bf-afbf-0637b9c02bbe' roleDefinitionIdOrName: 'Owner' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' } { + name: guid('Custom seed ${namePrefix}${serviceShort}') roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' principalId: nestedDependencies.outputs.managedIdentityPrincipalId principalType: 'ServicePrincipal' diff --git a/avm/res/web/static-site/version.json b/avm/res/web/static-site/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/web/static-site/version.json +++ b/avm/res/web/static-site/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 index bcaa55703a..32ef457548 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 @@ -72,6 +72,7 @@ function Initialize-DeploymentRemoval { # The initial sequence is a general order-recommendation $RemoveFirstSequence = @( 'Microsoft.Authorization/locks', + 'Microsoft.VirtualMachineImages/imageTemplates', # Must be removed before their MSI & should be removed before its entities permissions are removed 'Microsoft.Authorization/roleAssignments', 'Microsoft.Insights/diagnosticSettings', 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups', @@ -91,7 +92,6 @@ function Initialize-DeploymentRemoval { 'Microsoft.MachineLearningServices/workspaces', 'Microsoft.Compute/virtualMachines', 'Microsoft.ContainerInstance/containerGroups' # Must be removed before their MSI - 'Microsoft.VirtualMachineImages/imageTemplates', # Must be removed before their MSI 'Microsoft.ManagedIdentity/userAssignedIdentities', 'Microsoft.Databricks/workspaces' 'Microsoft.Resources/resourceGroups' diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 index 7c658659e0..1036e5f4d8 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 @@ -1,4 +1,109 @@ #region helper +<# +.SYNOPSIS +Get all deployment operations at a given scope + +.DESCRIPTION +Get all deployment oeprations at a given scope. By default, the results are filtered down to 'create' operations (i.e., excluding 'read' operations that would correspond to 'existing' resources). + +.PARAMETER Name +Mandatory. The deployment name to search for + +.PARAMETER ResourceGroupName +Optional. The name of the resource group for scope 'resourcegroup'. Relevant for resource-group-level deployments. + +.PARAMETER SubscriptionId +Optional. The ID of the subscription to fetch deployments from. Relevant for subscription- & resource-group-level deployments. + +.PARAMETER ManagementGroupId +Optional. The ID of the management group to fetch deployments from. Relevant for management-group-level deployments. + +.PARAMETER Scope +Mandatory. The scope to search in + +.PARAMETER ProvisioningOperationsToInclude +Optional. The provisioning operations to include in the result set. By default, only 'create' operations are included. + +.EXAMPLE +Get-DeploymentOperationAtScope -Scope 'subscription' -Name 'v73rhp24d7jya-test-apvmiaiboaai' + +Get all deployment operations for a deployment with name 'v73rhp24d7jya-test-apvmiaiboaai' at scope 'subscription' + +.NOTES +This function is a standin for the Get-AzDeploymentOperation cmdlet, which does not provide the ability to filter by provisioning operation. +As such, it was also returning 'existing' resources (i.e., with provisioningOperation=Read). +#> +function Get-DeploymentOperationAtScope { + + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [Alias('DeploymentName')] + [string] $Name, + + [Parameter(Mandatory = $false)] + [string] $ResourceGroupName, + + [Parameter(Mandatory = $false)] + [string] $SubscriptionId, + + [Parameter(Mandatory = $false)] + [string] $ManagementGroupId, + + [Parameter(Mandatory = $false)] + [ValidateSet( + 'Create', # any resource creation + 'Read', # E.g., 'existing' resources + 'EvaluateDeploymentOutput' # Nobody knows + )] + [string[]] $ProvisioningOperationsToInclude = @('Create'), + + [Parameter(Mandatory)] + [ValidateSet( + 'resourcegroup', + 'subscription', + 'managementgroup', + 'tenant' + )] + [string] $Scope + ) + + + switch ($Scope) { + 'resourcegroup' { + $path = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Resources/deployments/{2}/operations?api-version=2021-04-01' -f $SubscriptionId, $ResourceGroupName, $name + break + } + 'subscription' { + $path = '/subscriptions/{0}/providers/Microsoft.Resources/deployments/{1}/operations?api-version=2021-04-01' -f $SubscriptionId, $name + break + } + 'managementgroup' { + $path = '/providers/Microsoft.Management/managementGroups/{0}/providers/Microsoft.Resources/deployments/{1}/operations?api-version=2021-04-01' -f $ManagementGroupId, $name + break + } + 'tenant' { + $path = '/providers/Microsoft.Resources/deployments/{0}/operations?api-version=2021-04-01' -f $name + break + } + } + + ############################################## + # Get all deployment children based on scope # + ############################################## + + $response = Invoke-AzRestMethod -Method 'GET' -Path $path + + if ($response.StatusCode -ne 200) { + Write-Error ('Failed to fetch deployment operations for deployment [{0}] in scope [{1}]' -f $name, $scope) + return + } else { + $deploymentOperations = ($response.content | ConvertFrom-Json).value.properties + $deploymentOperationsFiltered = $deploymentOperations | Where-Object { $_.provisioningOperation -in $ProvisioningOperationsToInclude } + return $deploymentOperationsFiltered ?? $true # Returning true to indicate that the deployment was found, but did not contain any relevant operations + } +} + <# .SYNOPSIS Get all deployments that match a given deployment name in a given scope @@ -63,10 +168,18 @@ function Get-DeploymentTargetResourceListInner { ############################################## # Get all deployment children based on scope # ############################################## + $baseInputObject = @{ + Scope = $Scope + DeploymentName = $Name + } switch ($Scope) { 'resourcegroup' { if (Get-AzResourceGroup -Name $resourceGroupName -ErrorAction 'SilentlyContinue') { - [array]$deploymentTargets = (Get-AzResourceGroupDeploymentOperation -DeploymentName $name -ResourceGroupName $resourceGroupName).TargetResource | Where-Object { $_ -ne $null } | Select-Object -Unique + if ($op = Get-DeploymentOperationAtScope @baseInputObject -ResourceGroupName $resourceGroupName -SubscriptionId $currentContext.Subscription.Id) { + [array]$deploymentTargets = $op.TargetResource.id | Where-Object { $_ -ne $null } | Select-Object -Unique + } else { + throw 'NoDeploymentFound' + } } else { # In case the resource group itself was already deleted, there is no need to try and fetch deployments from it # In case we already have any such resources in the list, we should remove them @@ -75,15 +188,27 @@ function Get-DeploymentTargetResourceListInner { break } 'subscription' { - [array]$deploymentTargets = (Get-AzDeploymentOperation -DeploymentName $name).TargetResource | Where-Object { $_ -ne $null } | Select-Object -Unique + if ($op = Get-DeploymentOperationAtScope @baseInputObject -SubscriptionId $currentContext.Subscription.Id) { + [array]$deploymentTargets = $op.TargetResource.id | Where-Object { $_ -ne $null } | Select-Object -Unique + } else { + throw 'NoDeploymentFound' + } break } 'managementgroup' { - [array]$deploymentTargets = (Get-AzManagementGroupDeploymentOperation -DeploymentName $name -ManagementGroupId $ManagementGroupId).TargetResource | Where-Object { $_ -ne $null } | Select-Object -Unique + if ($op = Get-DeploymentOperationAtScope @baseInputObject -ManagementGroupId $ManagementGroupId) { + [array]$deploymentTargets = $op.TargetResource.id | Where-Object { $_ -ne $null } | Select-Object -Unique + } else { + throw 'NoDeploymentFound' + } break } 'tenant' { - [array]$deploymentTargets = (Get-AzTenantDeploymentOperation -DeploymentName $name).TargetResource | Where-Object { $_ -ne $null } | Select-Object -Unique + if ($op = Get-DeploymentOperationAtScope @baseInputObject) { + [array]$deploymentTargets = $op.TargetResource.id | Where-Object { $_ -ne $null } | Select-Object -Unique + } else { + throw 'NoDeploymentFound' + } break } } @@ -188,7 +313,8 @@ function Get-DeploymentTargetResourceList { [string] $ManagementGroupId, [Parameter(Mandatory = $true)] - [string] $Name, + [Alias('Name', 'Names', 'DeploymentName')] + [string[]] $DeploymentNames, [Parameter(Mandatory = $true)] [ValidateSet( @@ -207,31 +333,62 @@ function Get-DeploymentTargetResourceList { ) $searchRetryCount = 1 - do { - $innerInputObject = @{ - Name = $name - Scope = $scope - ErrorAction = 'SilentlyContinue' - } - if (-not [String]::IsNullOrEmpty($resourceGroupName)) { - $innerInputObject['resourceGroupName'] = $resourceGroupName + $resourcesToRemove = @() + $deploymentNameObjects = $DeploymentNames | ForEach-Object { + @{ + Name = $_ + Resolved = $false } - if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { - $innerInputObject['ManagementGroupId'] = $ManagementGroupId + } + + do { + foreach ($deploymentNameObject in $deploymentNameObjects) { + + if ($deploymentNameObject.Resolved) { + # Skip further invocations for this deployment name if deployment was already found + continue + } + + $innerInputObject = @{ + Name = $deploymentNameObject.Name + Scope = $scope + ErrorAction = 'SilentlyContinue' + } + if (-not [String]::IsNullOrEmpty($resourceGroupName)) { + $innerInputObject['resourceGroupName'] = $resourceGroupName + } + if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { + $innerInputObject['ManagementGroupId'] = $ManagementGroupId + } + try { + $targetResources = Get-DeploymentTargetResourceListInner @innerInputObject + Write-Verbose ('Found & resolved deployment [{0}]. [{1}] resources found to remove.' -f $deploymentNameObject.Name, $targetResources.Count) -Verbose + $deploymentNameObject.Resolved = $true + $resourcesToRemove += $targetResources + } catch { + $remainingDeploymentNames = ($deploymentNameObjects | Where-Object { -not $_.Resolved }).Name + Write-Verbose ('No deployment found by name(s) [{0}] in scope [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f ($remainingDeploymentNames -join ', '), $scope, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose + Start-Sleep $searchRetryInterval + $searchRetryCount++ + } } - [array]$targetResources = Get-DeploymentTargetResourceListInner @innerInputObject - if ($targetResources) { + + # Break check + if ($deploymentNameObjects.Resolved -notcontains $false) { break } - Write-Verbose ('No deployment found by name [{0}] in scope [{1}]. Retrying in [{2}] seconds [{3}/{4}]' -f $name, $scope, $searchRetryInterval, $searchRetryCount, $searchRetryLimit) -Verbose - Start-Sleep $searchRetryInterval - $searchRetryCount++ } while ($searchRetryCount -le $searchRetryLimit) - if (-not $targetResources) { - Write-Warning "No deployment target resources found for [$name]" - return @() - } + if ($searchRetryCount -gt $searchRetryLimit) { + $remainingDeploymentNames = ($deploymentNameObjects | Where-Object { -not $_.Resolved }).Name - return $targetResources + # We don't want to outright throw an exception as we want to remove as many resources as possible before failing the script in the calling function + return @{ + resolveError = ('No deployment for the deployment name(s) [{0}] found' -f ($remainingDeploymentNames -join ', ')) + resourcesToRemove = $resourcesToRemove + } + } + return @{ + resourcesToRemove = $resourcesToRemove + } } diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourcePostRemoval.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourcePostRemoval.ps1 index 25b068284f..4e13284417 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourcePostRemoval.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourcePostRemoval.ps1 @@ -11,6 +11,9 @@ Mandatory. The resourceID of the resource to remove .PARAMETER Type Mandatory. The type of the resource to remove +.PARAMETER PostRemovalRetryLimit +Optional. Specify the number of times the script should try to repeat the post-removal operation in case of a failure. + .EXAMPLE Invoke-ResourcePostRemoval -Type 'Microsoft.KeyVault/vaults' -ResourceId '/subscriptions/.../resourceGroups/validation-rg/providers/Microsoft.KeyVault/vaults/myVault' @@ -24,157 +27,169 @@ function Invoke-ResourcePostRemoval { [string] $ResourceId, [Parameter(Mandatory = $true)] - [string] $Type - ) + [string] $Type, - switch ($Type) { - 'Microsoft.AppConfiguration/configurationStores' { - $subscriptionId = $ResourceId.Split('/')[2] - $resourceName = Split-Path $ResourceId -Leaf + [Parameter(Mandatory = $false)] + [int] $PostRemovalRetryLimit = 3 + ) - # Fetch service in soft-delete - $getPath = '/subscriptions/{0}/providers/Microsoft.AppConfiguration/deletedConfigurationStores?api-version=2021-10-01-preview' -f $subscriptionId - $getRequestInputObject = @{ - Method = 'GET' - Path = $getPath - } - $softDeletedConfigurationStore = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.configurationStoreId -eq $ResourceId } - - if ($softDeletedConfigurationStore) { - # Purge service - $purgePath = '/subscriptions/{0}/providers/Microsoft.AppConfiguration/locations/{1}/deletedConfigurationStores/{2}/purge?api-version=2021-10-01-preview' -f $subscriptionId, $softDeletedConfigurationStore.properties.location, $resourceName - $purgeRequestInputObject = @{ - Method = 'POST' - Path = $purgePath - } - Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose - if ($PSCmdlet.ShouldProcess(('App Configuration Store with ID [{0}]' -f $softDeletedConfigurationStore.properties.configurationStoreId), 'Purge')) { - $response = Invoke-AzRestMethod @purgeRequestInputObject - if ($response.StatusCode -ne 200) { - throw ('Purge of resource [{0}] failed with error code [{1}]' -f $ResourceId, $response.StatusCode) + $removalRetryCount = 1 + do { + try { + switch ($Type) { + 'Microsoft.AppConfiguration/configurationStores' { + $subscriptionId = $ResourceId.Split('/')[2] + $resourceName = Split-Path $ResourceId -Leaf + + # Fetch service in soft-delete + $getPath = '/subscriptions/{0}/providers/Microsoft.AppConfiguration/deletedConfigurationStores?api-version=2021-10-01-preview' -f $subscriptionId + $getRequestInputObject = @{ + Method = 'GET' + Path = $getPath } - } - } - break - } - 'Microsoft.KeyVault/vaults' { - $resourceName = Split-Path $ResourceId -Leaf - - $matchingKeyVault = Get-AzKeyVault -InRemovedState | Where-Object { $_.resourceId -eq $ResourceId } - if ($matchingKeyVault -and -not $matchingKeyVault.EnablePurgeProtection) { - Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose - if ($PSCmdlet.ShouldProcess(('Key Vault with ID [{0}]' -f $matchingKeyVault.Id), 'Purge')) { - try { - $null = Remove-AzKeyVault -ResourceId $matchingKeyVault.Id -InRemovedState -Force -Location $matchingKeyVault.Location -ErrorAction 'Stop' - } catch { - if ($_.Exception.Message -like '*DeletedVaultPurge*') { - Write-Warning ('Purge protection for key vault [{0}] enabled. Skipping. Scheduled purge date is [{1}]' -f $resourceName, $matchingKeyVault.ScheduledPurgeDate) - } else { - throw $_ + $softDeletedConfigurationStore = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.configurationStoreId -eq $ResourceId } + + if ($softDeletedConfigurationStore) { + # Purge service + $purgePath = '/subscriptions/{0}/providers/Microsoft.AppConfiguration/locations/{1}/deletedConfigurationStores/{2}/purge?api-version=2021-10-01-preview' -f $subscriptionId, $softDeletedConfigurationStore.properties.location, $resourceName + $purgeRequestInputObject = @{ + Method = 'POST' + Path = $purgePath + } + Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose + if ($PSCmdlet.ShouldProcess(('App Configuration Store with ID [{0}]' -f $softDeletedConfigurationStore.properties.configurationStoreId), 'Purge')) { + $response = Invoke-AzRestMethod @purgeRequestInputObject + if ($response.StatusCode -ne 200) { + throw ('Purge of resource [{0}] failed with error code [{1}]' -f $ResourceId, $response.StatusCode) + } } } + break } - } - break - } - 'Microsoft.CognitiveServices/accounts' { - $resourceGroupName = $ResourceId.Split('/')[4] - $resourceName = Split-Path $ResourceId -Leaf - - $matchingAccount = Get-AzCognitiveServicesAccount -InRemovedState | Where-Object { $_.AccountName -eq $resourceName } - if ($matchingAccount) { - Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose - if ($PSCmdlet.ShouldProcess(('Cognitive services account with ID [{0}]' -f $matchingAccount.Id), 'Purge')) { - $null = Remove-AzCognitiveServicesAccount -InRemovedState -Force -Location $matchingAccount.Location -ResourceGroupName $resourceGroupName -Name $matchingAccount.AccountName - } - } - break - } - 'Microsoft.ApiManagement/service' { - $subscriptionId = $ResourceId.Split('/')[2] - $resourceName = Split-Path $ResourceId -Leaf - - # Fetch service in soft-delete - $getPath = '/subscriptions/{0}/providers/Microsoft.ApiManagement/deletedservices?api-version=2021-08-01' -f $subscriptionId - $getRequestInputObject = @{ - Method = 'GET' - Path = $getPath - } - $softDeletedService = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.serviceId -eq $ResourceId } - - if ($softDeletedService) { - # Purge service - $purgePath = '/subscriptions/{0}/providers/Microsoft.ApiManagement/locations/{1}/deletedservices/{2}?api-version=2020-06-01-preview' -f $subscriptionId, $softDeletedService.location, $resourceName - $purgeRequestInputObject = @{ - Method = 'DELETE' - Path = $purgePath + 'Microsoft.KeyVault/vaults' { + $resourceName = Split-Path $ResourceId -Leaf + + $matchingKeyVault = Get-AzKeyVault -InRemovedState | Where-Object { $_.resourceId -eq $ResourceId } + if ($matchingKeyVault -and -not $matchingKeyVault.EnablePurgeProtection) { + Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose + if ($PSCmdlet.ShouldProcess(('Key Vault with ID [{0}]' -f $matchingKeyVault.Id), 'Purge')) { + try { + $null = Remove-AzKeyVault -ResourceId $matchingKeyVault.Id -InRemovedState -Force -Location $matchingKeyVault.Location -ErrorAction 'Stop' + } catch { + if ($_.Exception.Message -like '*DeletedVaultPurge*') { + Write-Warning ('Purge protection for key vault [{0}] enabled. Skipping. Scheduled purge date is [{1}]' -f $resourceName, $matchingKeyVault.ScheduledPurgeDate) + } else { + throw $_ + } + } + } + } + break } - Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose - if ($PSCmdlet.ShouldProcess(('API management service with ID [{0}]' -f $softDeletedService.properties.serviceId), 'Purge')) { - $null = Invoke-AzRestMethod @purgeRequestInputObject + 'Microsoft.CognitiveServices/accounts' { + $resourceGroupName = $ResourceId.Split('/')[4] + $resourceName = Split-Path $ResourceId -Leaf + + $matchingAccount = Get-AzCognitiveServicesAccount -InRemovedState | Where-Object { $_.AccountName -eq $resourceName } + if ($matchingAccount) { + Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose + if ($PSCmdlet.ShouldProcess(('Cognitive services account with ID [{0}]' -f $matchingAccount.Id), 'Purge')) { + $null = Remove-AzCognitiveServicesAccount -InRemovedState -Force -Location $matchingAccount.Location -ResourceGroupName $resourceGroupName -Name $matchingAccount.AccountName + } + } + break } - } - break - } - 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems' { - # Remove protected VM - # Required if e.g. a VM was listed in an RSV and only that VM is removed - $vaultId = $ResourceId.split('/backupFabrics/')[0] - $resourceName = Split-Path $ResourceId -Leaf - $softDeleteStatus = (Get-AzRecoveryServicesVaultProperty -VaultId $vaultId).SoftDeleteFeatureState - if ($softDeleteStatus -ne 'Disabled') { - if ($PSCmdlet.ShouldProcess(('Soft-delete on RSV [{0}]' -f $vaultId), 'Set')) { - $null = Set-AzRecoveryServicesVaultProperty -VaultId $vaultId -SoftDeleteFeatureState 'Disable' + 'Microsoft.ApiManagement/service' { + $subscriptionId = $ResourceId.Split('/')[2] + $resourceName = Split-Path $ResourceId -Leaf + + # Fetch service in soft-delete + $getPath = '/subscriptions/{0}/providers/Microsoft.ApiManagement/deletedservices?api-version=2021-08-01' -f $subscriptionId + $getRequestInputObject = @{ + Method = 'GET' + Path = $getPath + } + $softDeletedService = ((Invoke-AzRestMethod @getRequestInputObject).Content | ConvertFrom-Json).value | Where-Object { $_.properties.serviceId -eq $ResourceId } + + if ($softDeletedService) { + # Purge service + $purgePath = '/subscriptions/{0}/providers/Microsoft.ApiManagement/locations/{1}/deletedservices/{2}?api-version=2020-06-01-preview' -f $subscriptionId, $softDeletedService.location, $resourceName + $purgeRequestInputObject = @{ + Method = 'DELETE' + Path = $purgePath + } + Write-Verbose ('[*] Purging resource [{0}] of type [{1}]' -f $resourceName, $Type) -Verbose + if ($PSCmdlet.ShouldProcess(('API management service with ID [{0}]' -f $softDeletedService.properties.serviceId), 'Purge')) { + $null = Invoke-AzRestMethod @purgeRequestInputObject + } + } + break } - } - - $backupItemInputObject = @{ - BackupManagementType = 'AzureVM' - WorkloadType = 'AzureVM' - VaultId = $vaultId - Name = $resourceName - } - if ($backupItem = Get-AzRecoveryServicesBackupItem @backupItemInputObject -ErrorAction 'SilentlyContinue') { - Write-Verbose (' [-] Removing Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId) -Verbose + 'Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems' { + # Remove protected VM + # Required if e.g. a VM was listed in an RSV and only that VM is removed + $vaultId = $ResourceId.split('/backupFabrics/')[0] + $resourceName = Split-Path $ResourceId -Leaf + $softDeleteStatus = (Get-AzRecoveryServicesVaultProperty -VaultId $vaultId).SoftDeleteFeatureState + if ($softDeleteStatus -ne 'Disabled') { + if ($PSCmdlet.ShouldProcess(('Soft-delete on RSV [{0}]' -f $vaultId), 'Set')) { + $null = Set-AzRecoveryServicesVaultProperty -VaultId $vaultId -SoftDeleteFeatureState 'Disable' + } + } - if ($backupItem.DeleteState -eq 'ToBeDeleted') { - if ($PSCmdlet.ShouldProcess('Soft-deleted backup data removal', 'Undo')) { - $null = Undo-AzRecoveryServicesBackupItemDeletion -Item $backupItem -VaultId $vaultId -Force + $backupItemInputObject = @{ + BackupManagementType = 'AzureVM' + WorkloadType = 'AzureVM' + VaultId = $vaultId + Name = $resourceName } - } + if ($backupItem = Get-AzRecoveryServicesBackupItem @backupItemInputObject -ErrorAction 'SilentlyContinue') { + Write-Verbose (' [-] Removing Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId) -Verbose - if ($PSCmdlet.ShouldProcess(('Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId), 'Remove')) { - $null = Disable-AzRecoveryServicesBackupProtection -Item $backupItem -VaultId $vaultId -RemoveRecoveryPoints -Force - } - } + if ($backupItem.DeleteState -eq 'ToBeDeleted') { + if ($PSCmdlet.ShouldProcess('Soft-deleted backup data removal', 'Undo')) { + $null = Undo-AzRecoveryServicesBackupItemDeletion -Item $backupItem -VaultId $vaultId -Force + } + } - # Undo a potential soft delete state change - $null = Set-AzRecoveryServicesVaultProperty -VaultId $vaultId -SoftDeleteFeatureState $softDeleteStatus.TrimEnd('d') - break - } - 'Microsoft.Databricks/workspaces' { - $resourceGroupName = $ResourceId.Split('/')[4] - $resourceName = Split-Path $ResourceId -Leaf - # If the `managedResourceGroupResourceId` parameter was set during deployment, we should look for that resource group and remove it as it is not automatically removed - # NOTE: This requires that the provided value uses the prefix `rg-` and suffix '-managed' - $managedResourceGroupName = "rg-$resourceGroupName-managed" - if (Get-AzResourceGroup -Name $managedResourceGroupName -ErrorAction 'SilentlyContinue') { - Write-Verbose ('[*] Removing managed resource group [{0}] of workspace [{1}]' -f $managedResourceGroupName, $resourceName) -Verbose - if ($PSCmdlet.ShouldProcess(('Managed resource group [{0}]' -f $managedResourceGroupName), 'Remove')) { - $null = Remove-AzResourceGroup -Name $managedResourceGroupName -Force + if ($PSCmdlet.ShouldProcess(('Backup item [{0}] from RSV [{1}]' -f $backupItem.Name, $vaultId), 'Remove')) { + $null = Disable-AzRecoveryServicesBackupProtection -Item $backupItem -VaultId $vaultId -RemoveRecoveryPoints -Force + } + } + + # Undo a potential soft delete state change + $null = Set-AzRecoveryServicesVaultProperty -VaultId $vaultId -SoftDeleteFeatureState $softDeleteStatus.TrimEnd('d') + break } - } - # If the `managedResourceGroupResourceId` parameter was NOT set during deployment, we should look for a resource group with the default name and remove it as it is not automatically removed - # NOTE: This requires that the default value uses the prefix 'rg-' - $defaultManagedResourceGroupName = "rg-$resourceName-managed" - if (Get-AzResourceGroup -Name $defaultManagedResourceGroupName -ErrorAction 'SilentlyContinue') { - Write-Verbose ('[*] Removing managed resource group [{0}] of workspace [{1}]' -f $defaultManagedResourceGroupName, $resourceName) -Verbose - if ($PSCmdlet.ShouldProcess(('Managed resource group [{0}]' -f $defaultManagedResourceGroupName), 'Remove')) { - $null = Remove-AzResourceGroup -Name $defaultManagedResourceGroupName -Force + 'Microsoft.Databricks/workspaces' { + $resourceGroupName = $ResourceId.Split('/')[4] + $resourceName = Split-Path $ResourceId -Leaf + # If the `managedResourceGroupResourceId` parameter was set during deployment, we should look for that resource group and remove it as it is not automatically removed + # NOTE: This requires that the provided value uses the prefix `rg-` and suffix '-managed' + $managedResourceGroupName = "rg-$resourceGroupName-managed" + if (Get-AzResourceGroup -Name $managedResourceGroupName -ErrorAction 'SilentlyContinue') { + Write-Verbose ('[*] Removing managed resource group [{0}] of workspace [{1}]' -f $managedResourceGroupName, $resourceName) -Verbose + if ($PSCmdlet.ShouldProcess(('Managed resource group [{0}]' -f $managedResourceGroupName), 'Remove')) { + $null = Remove-AzResourceGroup -Name $managedResourceGroupName -Force + } + } + # If the `managedResourceGroupResourceId` parameter was NOT set during deployment, we should look for a resource group with the default name and remove it as it is not automatically removed + # NOTE: This requires that the default value uses the prefix 'rg-' + $defaultManagedResourceGroupName = "rg-$resourceName-managed" + if (Get-AzResourceGroup -Name $defaultManagedResourceGroupName -ErrorAction 'SilentlyContinue') { + Write-Verbose ('[*] Removing managed resource group [{0}] of workspace [{1}]' -f $defaultManagedResourceGroupName, $resourceName) -Verbose + if ($PSCmdlet.ShouldProcess(('Managed resource group [{0}]' -f $defaultManagedResourceGroupName), 'Remove')) { + $null = Remove-AzResourceGroup -Name $defaultManagedResourceGroupName -Force + } + } + break } + ### CODE LOCATION: Add custom post-removal operation here } - break + break # Post-removal was successful + } catch { + Write-Warning ('[!] Post-removal operation failed. Reason: [{0}]. Retry [{1}/{2}]' -f $_.Exception.Message, $removalRetryCount, $PostRemovalRetryLimit) + $removalRetryCount++ } - ### CODE LOCATION: Add custom post-removal operation here - } + } while ($removalRetryCount -le $PostRemovalRetryLimit) } diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 index 00a0d50676..df9eaf45f6 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 @@ -90,30 +90,31 @@ function Remove-Deployment { # Fetch deployments # ================= - - foreach ($deploymentName in $DeploymentNames) { - $deploymentsInputObject = @{ - Name = $deploymentName - Scope = $deploymentScope - } - if (-not [String]::IsNullOrEmpty($ResourceGroupName)) { - $deploymentsInputObject['resourceGroupName'] = $ResourceGroupName - } - if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { - $deploymentsInputObject['ManagementGroupId'] = $ManagementGroupId - } - $deployedTargetResources += Get-DeploymentTargetResourceList @deploymentsInputObject + $deploymentsInputObject = @{ + DeploymentNames = $DeploymentNames + Scope = $deploymentScope } - - if ($deployedTargetResources.Count -eq 0) { - throw 'No deployment target resources found.' + if (-not [String]::IsNullOrEmpty($ResourceGroupName)) { + $deploymentsInputObject['resourceGroupName'] = $ResourceGroupName + } + if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { + $deploymentsInputObject['ManagementGroupId'] = $ManagementGroupId } + + # In case the function also returns an error, we'll throw a corresponding exception at the end of this script (see below) + $resolveResult = Get-DeploymentTargetResourceList @deploymentsInputObject + $deployedTargetResources += $resolveResult.resourcesToRemove } [array] $deployedTargetResources = $deployedTargetResources | Select-Object -Unique Write-Verbose ('Total number of deployment target resources after fetching deployments [{0}]' -f $deployedTargetResources.Count) -Verbose + if (-not $deployedTargetResources) { + # Nothing to do + return + } + # Pre-Filter & order items # ======================== $rawTargetResourceIdsToRemove = $deployedTargetResources | Sort-Object -Culture 'en-US' -Property { $_.Split('/').Count } -Descending | Select-Object -Unique @@ -124,10 +125,6 @@ function Remove-Deployment { [array] $resourcesToRemove = Get-ResourceIdsAsFormattedObjectList -ResourceIds $rawTargetResourceIdsToRemove Write-Verbose ('Total number of deployment target resources after formatting items [{0}]' -f $resourcesToRemove.Count) -Verbose - if ($resourcesToRemove.Count -eq 0) { - return - } - # Filter resources # ================ @@ -175,6 +172,11 @@ function Remove-Deployment { } else { Write-Verbose 'Found [0] resources to remove' } + + # In case any deployment was not resolved as planned we finally want to throw an exception to make this visible in the pipeline + if ($resolveResult.resolveError) { + throw ('The following error was thrown while resolving the original deployment names: [{0}]' -f $resolveResult.resolveError) + } } end { diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-ResourceList.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-ResourceList.ps1 index 964676b611..1f6e3ba055 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-ResourceList.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-ResourceList.ps1 @@ -36,7 +36,7 @@ function Remove-ResourceListInner { foreach ($resource in $resourcesToRemove) { $resourceName = Split-Path $resource.resourceId -Leaf - $alreadyProcessed = $processedResources.count -gt 0 ? (($processedResources | Where-Object { $resource.resourceId -like ('{0}*' -f $_) }).Count -gt 0) : $false + $alreadyProcessed = $processedResources.count -gt 0 ? (($processedResources | Where-Object { $resource.resourceId -like ('{0}/*' -f $_) }).Count -gt 0) : $false if ($alreadyProcessed) { # Skipping @@ -130,6 +130,6 @@ function Remove-ResourceList { if ($resourcesToRetry.Count -gt 0) { throw ('The removal failed for resources [{0}]' -f ((Split-Path $resourcesToRetry.resourceId -Leaf) -join ', ')) } else { - Write-Verbose 'The removal completed successfully' + Write-Verbose ('The removal of the [{0}] completed successfully' -f $ResourcesToRemove.Count) } } diff --git a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 index ebd9ea4f04..03dd99c99c 100644 --- a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 +++ b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 @@ -100,6 +100,9 @@ function Invoke-AvmJsonModuleIndexGeneration { } $tags = $tagListResponse.tags | Sort-Object -Culture 'en-US' + # Sort tags by order of semantic versioning with the latest version last + $tags = $tags | Sort-Object -Property { [semver] $_ } + $properties = [ordered]@{} foreach ($tag in $tags) { $gitTag = "$modulePath/$tag" diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubPrLabels.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubPrLabels.ps1 new file mode 100644 index 0000000000..a22fabe460 --- /dev/null +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubPrLabels.ps1 @@ -0,0 +1,75 @@ +<# +.SYNOPSIS +Checks new PRs and adds labels, depending if module owner can approve, or core team is needed + +.DESCRIPTION +For the given PR, the "needs core team" label will be added, if the module is orphaned or the sole module owner is creating the PR + +.PARAMETER Repo +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. + +.PARAMETER PrUrl +Mandatory. The URL of the GitHub pull request, like 'https://github.com/Azure/bicep-registry-modules/pull/2540' + +.EXAMPLE +Set-AvmGitHubPrLabels -Repo 'Azure/bicep-registry-modules' -PrUrl 'https://github.com/Azure/bicep-registry-modules/pull/2540' + +.NOTES +Will be triggered by the corresponding platform workflow +#> +function Set-AvmGitHubPrLabels { + param ( + [Parameter(Mandatory = $true)] + [string] $Repo, + + [Parameter(Mandatory = $true)] + [string] $PrUrl, + + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName + ) + + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-GithubPrRequestedReviewerTeamNames.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-GithubTeamMembersLogin.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + + $sanitizedPrUrl = $PrUrl.Replace('api.', '').Replace('repos/', '').Replace('pulls/', 'pull/') + $pr = gh pr view $sanitizedPrUrl --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 + $allTeamNames = [array](Get-GithubPrRequestedReviewerTeamNames -PrUrl $pr.url) + $teamNames = [array]($allTeamNames | Where-Object { $_ -ne 'bicep-admins' -and $_ -ne 'avm-core-team-technical-bicep' -and $_ -ne 'avm-module-reviewers-bicep' }) + + if ($allTeamNames.Count -gt 0) { + # core team is already assigned, no or more than one module reviewer team is assigned + if ($allTeamNames.Contains('avm-core-team-technical-bicep') -or $teamNames.Count -eq 0 -or $teamNames.Count -gt 1) { + gh pr edit $pr.url --add-label 'Needs: Core Team :genie:' --repo $Repo + } else { + $teamMembers = [array](Get-GithubTeamMembersLogin -OrgName $Repo.Split('/')[0] -TeamName $teamNames[0]) + + # no members in module team or only one and that user submitted the PR + if (($teamMembers.Count -eq 0) -or ($teamMembers.Count -eq 1 -and $teamMembers[0] -eq $pr.author.login)) { + gh pr edit $pr.url --add-label 'Needs: Core Team :genie:' --repo $Repo + } else { + gh pr edit $pr.url --add-label 'Needs: Module Owner :mega:' --repo $Repo + } + + # check for orphanded module + $moduleName = $teamNames[0] + $moduleIndex = $moduleName.StartsWith('avm-res') ? 'Bicep-Resource' : ($moduleName.StartsWith('avm-ptn') ? 'Bicep-Pattern' : 'Bicep-Utility') + # get CSV data + $modules = Get-AvmCsvData -ModuleIndex $moduleIndex + + foreach ($module in $modules) { + if ($module.ModuleName.Replace('-', '').Replace('/', '-') -eq $moduleName) { + if ($module.ModuleStatus -eq 'Orphaned :eyes:') { + gh pr edit $pr.url --add-label 'Status: Module Orphaned :eyes:' --repo $Repo + break; + } + } + } + } + } +} diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index 28bb0dca15..07c0a28295 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -88,6 +88,7 @@ function Set-AvmGithubIssueForWorkflow { if ($workflowRun.conclusion -eq 'failure') { $issueName = "[Failed pipeline] $($workflowRun.workflowName)" $failedrun = "Failed run: $($workflowRun.url)" + $moduleName = $workflowRun.workflowName.Replace('.', '/') if ($issues.title -notcontains $issueName) { if ($PSCmdlet.ShouldProcess("Issue [$issueName]", 'Create')) { @@ -100,7 +101,6 @@ function Set-AvmGithubIssueForWorkflow { "@ if ($workflowRun.workflowName -match 'avm.(?:res|ptn)') { - $moduleName = $workflowRun.workflowName.Replace('.', '/') $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' # get CSV data $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName diff --git a/avm/utilities/pipelines/platform/deploymentRemoval/Clear-ManagementGroupDeploymentHistory.ps1 b/avm/utilities/pipelines/platform/deploymentRemoval/Clear-ManagementGroupDeploymentHistory.ps1 index f84d790395..9bbf51e1a0 100644 --- a/avm/utilities/pipelines/platform/deploymentRemoval/Clear-ManagementGroupDeploymentHistory.ps1 +++ b/avm/utilities/pipelines/platform/deploymentRemoval/Clear-ManagementGroupDeploymentHistory.ps1 @@ -50,10 +50,15 @@ function Clear-ManagementGroupDeploymentHistory { Method = 'GET' Uri = "https://management.azure.com/providers/Microsoft.Management/managementGroups/$ManagementGroupId/providers/Microsoft.Resources/deployments/?api-version=2021-04-01" Headers = @{ - Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token + Authorization = 'Bearer {0}' -f ((Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText) } } - $response = Invoke-RestMethod @getInputObject + try { + $response = Invoke-RestMethod @getInputObject + } catch { + Write-Warning "Management Group [$ManagementGroupId] not found or not authorized. Skipping removal." + return + } if (($response | Get-Member -MemberType 'NoteProperty').Name -notcontains 'value') { throw ('Fetching deployments failed with error [{0}]' -f ($reponse | Out-String)) @@ -103,7 +108,7 @@ function Clear-ManagementGroupDeploymentHistory { Method = 'POST' Uri = 'https://management.azure.com/batch?api-version=2020-06-01' Headers = @{ - Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token + Authorization = 'Bearer {0}' -f ((Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText) 'Content-Type' = 'application/json' } Body = @{ diff --git a/avm/utilities/pipelines/platform/deploymentRemoval/Clear-SubscriptionDeploymentHistory.ps1 b/avm/utilities/pipelines/platform/deploymentRemoval/Clear-SubscriptionDeploymentHistory.ps1 index 234bd4139f..e6e0b10dfd 100644 --- a/avm/utilities/pipelines/platform/deploymentRemoval/Clear-SubscriptionDeploymentHistory.ps1 +++ b/avm/utilities/pipelines/platform/deploymentRemoval/Clear-SubscriptionDeploymentHistory.ps1 @@ -54,7 +54,7 @@ function Clear-SubscriptionDeploymentHistory { Method = 'GET' Uri = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Resources/deployments?api-version=2020-06-01" Headers = @{ - Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token + Authorization = 'Bearer {0}' -f ((Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText) } } $response = Invoke-RestMethod @getInputObject @@ -107,7 +107,7 @@ function Clear-SubscriptionDeploymentHistory { Method = 'POST' Uri = 'https://management.azure.com/batch?api-version=2020-06-01' Headers = @{ - Authorization = 'Bearer {0}' -f (Get-AzAccessToken).Token + Authorization = 'Bearer {0}' -f ((Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText) 'Content-Type' = 'application/json' } Body = @{ diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 index 4e150984eb..825d353058 100644 --- a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -6,7 +6,7 @@ Parses AVM module CSV file Depending on the parameter, the correct CSV file will be parsed and returned a an object .PARAMETER ModuleIndex -Mandatory. Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern') +Mandatory. Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern', 'Bicep-Utility') .EXAMPLE Get-AvmCsvData -ModuleIndex 'Bicep-Resource' @@ -17,13 +17,14 @@ Function Get-AvmCsvData { [CmdletBinding()] param ( [Parameter(Mandatory)] - [ValidateSet('Bicep-Resource', 'Bicep-Pattern')] + [ValidateSet('Bicep-Resource', 'Bicep-Pattern', 'Bicep-Utility')] [string] $ModuleIndex ) # CSV file URLs $BicepResourceUrl = 'https://aka.ms/avm/index/bicep/res/csv' $BicepPatternUrl = 'https://aka.ms/avm/index/bicep/ptn/csv' + $BicepUtilityUrl = 'https://aka.ms/avm/index/bicep/utl/csv' # Retrieve the CSV file switch ($ModuleIndex) { @@ -41,6 +42,13 @@ Function Get-AvmCsvData { throw 'Unable to retrieve CSV file - Check network connection.' } } + 'Bicep-Utility' { + try { + $unfilteredCSV = Invoke-WebRequest -Uri $BicepUtilityUrl + } catch { + throw 'Unable to retrieve CSV file - Check network connection.' + } + } } # Convert the CSV content to a PowerShell object diff --git a/avm/utilities/pipelines/platform/helper/Get-GithubPrRequestedReviewerTeamNames.ps1 b/avm/utilities/pipelines/platform/helper/Get-GithubPrRequestedReviewerTeamNames.ps1 new file mode 100644 index 0000000000..c97927ad60 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Get-GithubPrRequestedReviewerTeamNames.ps1 @@ -0,0 +1,27 @@ +<# +.SYNOPSIS +Gets all requested reviewer names that are teams (not individual users) for a given GitHub pull request + +.DESCRIPTION +Gets all requested reviewer names that are teams (not individual users) for a given GitHub pull request + +.PARAMETER PrUrl +Mandatory. The GitHub pull request URL (for example https://github.com/Azure/bicep-registry-modules/pull/2540) + +.EXAMPLE +Get-GithubPrRequestedReviewerTeamNames -PrUrl 'https://github.com/Azure/bicep-registry-modules/pull/2540' + +.NOTES +Needs to run under a context with the permissions to read pull requests +#> +function Get-GithubPrRequestedReviewerTeamNames { + param ( + [Parameter(Mandatory = $true)] + [string] $PrUrl + ) + + $modifiedPrUrl = ($PrUrl.Replace('https://github.com', '/repos').Replace('/pull', '/pulls') + '/requested_reviewers') + $reviewers = (gh api -H 'Accept: application/vnd.github+json' -H 'X-GitHub-Api-Version: 2022-11-28' $modifiedPrUrl) | ConvertFrom-Json -Depth 100 + + return $reviewers.teams | Select-Object -ExpandProperty name +} diff --git a/avm/utilities/pipelines/platform/helper/Get-GithubTeamMembersLogin.ps1 b/avm/utilities/pipelines/platform/helper/Get-GithubTeamMembersLogin.ps1 new file mode 100644 index 0000000000..5ab8c52e85 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Get-GithubTeamMembersLogin.ps1 @@ -0,0 +1,32 @@ +<# +.SYNOPSIS +Gets the login name of all team members for a given GitHub team + +.DESCRIPTION +Gets the login name of all team members for a given GitHub team + +.PARAMETER OrgName +Mandatory. The name of the GitHub organization + +.PARAMETER TeamName +Mandatory. The name of the GitHub team (from that organization) + +.EXAMPLE +Get-GithubTeamMembersLogin -OrgName 'Azure' -TeamName 'avm-core-team-technical-bicep' + +.NOTES +Needs to run under a context with the permissions to read organisations directory +#> +function Get-GithubTeamMembersLogin { + param ( + [Parameter(Mandatory = $true)] + [string] $OrgName, + + [Parameter(Mandatory = $true)] + [string] $TeamName + ) + + $teamMembers = (gh api -H 'Accept: application/vnd.github+json' -H 'X-GitHub-Api-Version: 2022-11-28' /orgs/$OrgName/teams/$TeamName/members) | ConvertFrom-Json -Depth 100 + + return $teamMembers | Select-Object -ExpandProperty login +} diff --git a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 index f94b994249..837b68ecb1 100644 --- a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 +++ b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 @@ -595,7 +595,7 @@ function Set-DataCollectionSection { ) # Load content, if required - if ($PreLoadedContent.Keys -notcontains 'TelemetryFileContent') { + if ($PreLoadedContent.Keys -notcontains 'TelemetryFileContent' -or -not $PreLoadedContent['TelemetryFileContent']) { $telemetryUrl = 'https://aka.ms/avm/static/telemetry' try { diff --git a/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1 b/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1 index b71fb9d849..eab33e9c53 100644 --- a/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1 +++ b/avm/utilities/pipelines/sharedScripts/helper/Get-SpecsAlignedResourceName.ps1 @@ -116,7 +116,7 @@ function Get-SpecsAlignedResourceName { $resourceType = $innerResourceTypes | Where-Object { $_ -match $fallbackResourceTypeRegex } if (-not $resourceType) { # if we still don't find anything (because the resource type straight up does not exist, we fall back to itself as the default) - Write-Warning "Resource type [$rawResourceType] does not exist in the API / is custom. Falling back to it as default." + Write-Warning "Resource type [$rawResourceType] cannot be found or does not exist in the API specs / is custom. Falling back to it as default." $resourceType = $rawResourceType } else { Write-Warning ('Failed to find exact match between core matched resource types and [{0}]. Fallback on [{1}].' -f $rawResourceType, (Split-Path $rawResourceType -Parent)) diff --git a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 index baddac83cd..a8b4d77db7 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 @@ -5,6 +5,9 @@ Parse a Pester output containing checks & results and generate formatted markdow .DESCRIPTION Parse a Pester output containing checks & results and generate formatted markdown file out of it. +.PARAMETER RepoRootPath +Optional. The path to the root of the repository + .PARAMETER PesterTestResults Mandatory. The Pester tests results to parse. Can be fetched by running Pester with the `-PassThru` parameter. For example: @@ -68,6 +71,9 @@ function Set-PesterGitHubOutput { [CmdletBinding(SupportsShouldProcess)] param ( + [Parameter(Mandatory = $false)] + [string] $RepoRootPath = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.Parent.FullName, + [Parameter(Mandatory = $true)] [PSCustomObject] $PesterTestResults, @@ -94,6 +100,8 @@ function Set-PesterGitHubOutput { Write-Verbose ('Formatting [{0}] skipped tests' -f $skippedTests.Count) Write-Verbose ('Formatting [{0}] tests with explicit warnings' -f $warnings.Count) + $moduleSplitRegex = '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' + ###################### # Set output content # ###################### @@ -108,7 +116,7 @@ function Set-PesterGitHubOutput { $fileContent += [System.Collections.ArrayList]@( '| Total No. of Processed Tests| Passed Tests :white_check_mark: | Failed Tests :x: | Skipped Tests :paperclip: | Tests with warnings :warning: |', '| :-- | :-- | :-- | :-- | :-- |', - ('| {0} | {1} | {2} | {3} | {4} |' -f $PesterTestResults.TotalCount, $passedTests.count , $failedTests.count, $skippedTests.count, $testsWithWarnings.count), + ('| {0} | {1} | {2} | {3} | {4} |' -f $PesterTestResults.TotalCount, $passedTests.count , $failedTests.count, $skippedTests.count, $testsWithWarnings.count), '' ) @@ -134,16 +142,23 @@ function Set-PesterGitHubOutput { $intermediateNameElements[-1] = '**{0}**' -f $failedTest.ExpandedName $testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim() - $errorTestLine = $failedTest.ErrorRecord.TargetObject.Line - $errorFileIdentifier = $failedTest.ErrorRecord.TargetObject.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' - $errorTestFile = ('avm/{0}/{1}' -f $errorFileIdentifier[1], $errorFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + if ($failedTest.ScriptBlock.File -match $moduleSplitRegex) { + # Module test + $testFileIdentifier = $failedTest.ErrorRecord.TargetObject.File -split $moduleSplitRegex + $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + } else { + # None-module test + $testFile = $failedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath)) + } + + $testLine = $failedTest.ErrorRecord.TargetObject.Line $errorMessage = ($failedTest.ErrorRecord.TargetObject.Message.Trim() -replace '_', '\_') -replace '\n', '
    ' # Replace new lines with
    to enable line breaks in markdown - $testReference = '{0}:{1}' -f (Split-Path $errorTestFile -Leaf), $errorTestLine + $testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) { # Creating URL to test file to enable users to 'click' on it - $testReference = "[$testReference](https://github.com/$GitHubRepository/blob/$BranchName/$errorTestFile#L$errorTestLine)" + $testReference = "[$testReference](https://github.com/$GitHubRepository/blob/$BranchName/$testFile#L$testLine)" } $fileContent += '| {0} | {1} | {2} |' -f $testName, $errorMessage, $testReference @@ -181,10 +196,16 @@ function Set-PesterGitHubOutput { $intermediateNameElements[-1] = '**{0}**' -f $passedTest.ExpandedName $testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim() - $testLine = $passedTest.ScriptBlock.StartPosition.StartLine - $testFileIdentifier = $passedTest.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' - $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + if ($passedTest.ScriptBlock.File -match $moduleSplitRegex) { + # Module test + $testFileIdentifier = $passedTest.ScriptBlock.File -split $moduleSplitRegex + $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + } else { + # None-module test + $testFile = $passedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath)) + } + $testLine = $passedTest.ScriptBlock.StartPosition.StartLine $testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) { # Creating URL to test file to enable users to 'click' on it @@ -229,10 +250,16 @@ function Set-PesterGitHubOutput { $reason = ('Test {0}' -f $skippedTest.ErrorRecord.Exception.Message -replace '\|', '\|').Trim() - $testLine = $skippedTest.ScriptBlock.StartPosition.StartLine - $testFileIdentifier = $skippedTest.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' - $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + if ($skippedTest.ScriptBlock.File -match $moduleSplitRegex) { + # Module test + $testFileIdentifier = $skippedTest.ScriptBlock.File -split $moduleSplitRegex + $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + } else { + # None-module test + $testFile = $skippedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath)) + } + $testLine = $skippedTest.ScriptBlock.StartPosition.StartLine $testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) { # Creating URL to test file to enable users to 'click' on it @@ -275,10 +302,16 @@ function Set-PesterGitHubOutput { $intermediateNameElements[-1] = '**{0}**' -f $test.ExpandedName $testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim() - $testLine = $test.ScriptBlock.StartPosition.StartLine - $testFileIdentifier = $test.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' - $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + if ($test.ScriptBlock.File -match $moduleSplitRegex) { + # Module test + $testFileIdentifier = $test.ScriptBlock.File -split $moduleSplitRegex + $testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1] + } else { + # None-module test + $testFile = $test.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath)) + } + $testLine = $test.ScriptBlock.StartPosition.StartLine $testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) { # Creating URL to test file to enable users to 'click' on it diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rules.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rules.yaml deleted file mode 100644 index c3bb28aae8..0000000000 --- a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rules.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Synopsis: Use a zone or geo redundant storage account -apiVersion: github.com/microsoft/PSRule/v1 -kind: Rule -metadata: - name: 'Custom.Azure.Storage.UseReplication' -spec: - type: - - Microsoft.Storage/storageAccounts - condition: - anyOf: - - field: 'Sku.name' - contains: 'ZRS' - - field: 'Sku.name' - contains: 'GRS' diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml index 5e9c67b778..603250a1e1 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml @@ -43,6 +43,9 @@ spec: - Azure.FrontDoor.Probe # Supressed as the probe is being provided as parameter and we are not able to enforce as default value - Azure.FrontDoor.ProbeMethod # Supressed as the probe method is being provided as parameter and we are not able to enforce as default value - Azure.FrontDoor.ProbePath # Supressed as the probe path is being provided as parameter and we are not able to enforce as default value + # Azure API Management + - Azure.APIM.MultiRegion # Team agreed this is too expensive for most use cases and is safe to ignore. Would require dependencies for a min deployment. + - Azure.APIM.ManagedIdentity if: name: "." contains: diff --git a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml index 93adbe11ca..cde38fa9db 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml @@ -16,7 +16,7 @@ binding: # Require minimum versions of modules. requires: PSRule: "@pre >=2.9.0" - PSRule.Rules.Azure: "@pre >=1.29.0" + PSRule.Rules.Azure: "@pre >=1.38.0" # Use PSRule for Azure. include: @@ -74,11 +74,10 @@ configuration: rule: # Enable custom rules that don't exist in the baseline - includeLocal: true + includeLocal: false exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - Azure.VM.UseHybridUseBenefit - - Azure.Storage.UseReplication - Azure.AppConfig.PurgeProtect - - Azure.APIM.MultiRegion # Team agreed this is too expensive for most use cases and is safe to ignore. + - Azure.MySQL.MaintenanceWindow # Must be excluded until https://msdata.visualstudio.com/Database%20Systems/_workitems/edit/2788114 is fixed diff --git a/avm/utilities/tests/Test-CI.ps1 b/avm/utilities/tests/Test-CI.ps1 new file mode 100644 index 0000000000..b6762004e8 --- /dev/null +++ b/avm/utilities/tests/Test-CI.ps1 @@ -0,0 +1,111 @@ +<# +.SYNOPSIS +This script executes either all CI-focused Pester tests, or a specific test file and generates a markdown with the test results. + +.DESCRIPTION +This script executes either all CI-focused Pester tests, or a specific test file and generates a markdown with the test results. +It only considers test files in its current or sub-folders + +.PARAMETER RepoRootPath +Optional. The root of the repository. Used to correctly resolve paths. + +.PARAMETER BranchName +Optional. The branch to test for. + +.PARAMETER GitHubRepository +Optional. The repository containing the test file. If provided it will be used to generate a URL to the exact line of the test. +For example: 'Azure/ResourceModules' + +.PARAMETER TestFileRegex +Optional. The regex to use when searching for test files + +.PARAMETER SkipOutput +Optional. Set if you don't want the script to generate the results markdown + +.EXAMPLE +Test-CI + +Invoke all CI-focused Pester tests and generate a markdown file with the test results. + +.EXAMPLE +Test-CI -TestFileRegex '.*Ordered.*' -SkipOutput + +Invoke all CI-focused Pester tests that match the regex '.*Ordered.*' skip the generation of the markdown file containing the test results. +#> +function Test-CI { + + [CmdletBinding()] + param ( + [Parameter()] + [string] $RepoRootPath = (Get-Item $PSScriptRoot).Parent.Parent.Parent.FullName, + + [Parameter()] + [string] $BranchName = (git branch --show-current), + + [Parameter(Mandatory )] + [string] $GitHubRepository, + + [Parameter()] + [string] $TestFileRegex = '.*', + + [Parameter()] + [switch] $SkipOutput + ) + + # Load used functions + . (Join-Path $RepoRootPath 'avm' 'utilities' 'pipelines' 'staticValidation' 'compliance' 'Set-PesterGitHubOutput.ps1') + + $testFiles = (Get-ChildItem -Path (Join-Path $RepoRootPath 'avm' 'utilities' 'tests') -Recurse -File -Filter '*.tests.ps1').FullName | Where-Object { + $_ -match $TestFileRegex + } + + if (-not $testFiles) { + Write-Warning "Skipping test execution as no test files matching the regex [$TestFileRegex] were found." + return + } + + # ------------------- # + # Invoke Pester tests # + # ------------------- # + $pesterConfiguration = @{ + Run = @{ + Container = New-PesterContainer -Path $testFiles -Data @{ + RepoRootPath = $RepoRootPath + } + PassThru = $true + } + Output = @{ + Verbosity = 'Detailed' + } + } + + Write-Verbose 'Invoke test with' -Verbose + Write-Verbose ($pesterConfiguration | ConvertTo-Json -Depth 4 | Out-String) -Verbose + + $testResults = Invoke-Pester -Configuration $pesterConfiguration + + if (-not $SkipOutput) { + # ----------------------------------------- # + # Create formatted Pester Test Results File # + # ----------------------------------------- # + + $functionInput = @{ + RepoRootPath = $RepoRootPath + PesterTestResults = $testResults + OutputFilePath = Join-Path $RepoRootPath 'avm' 'utilities' 'tests' 'Pester-output.md' + GitHubRepository = $GitHubRepository + BranchName = $BranchName + } + + Write-Verbose 'Invoke Pester formatting function with' -Verbose + Write-Verbose ($functionInput | ConvertTo-Json -Depth 0 | Out-String) -Verbose + + Set-PesterGitHubOutput @functionInput -Verbose + + return $functionInput.OutputFilePath + } +} + + + + diff --git a/avm/utilities/tests/pipelines/Get-OrderedResourcesList.tests.ps1 b/avm/utilities/tests/pipelines/Get-OrderedResourcesList.tests.ps1 new file mode 100644 index 0000000000..37c725b590 --- /dev/null +++ b/avm/utilities/tests/pipelines/Get-OrderedResourcesList.tests.ps1 @@ -0,0 +1,215 @@ +param( + [Parameter(Mandatory = $false)] + [string] $repoRootPath = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.FullName +) + +$script:repoRootPath = $repoRootPath + +Describe 'Test sequence ordering' { + + BeforeEach { + . (Join-Path $repoRootPath 'avm' 'utilities' 'pipelines' 'e2eValidation' 'resourceRemoval' 'helper' 'Get-OrderedResourcesList.ps1') + } + + It 'Remove first sequence should be as expected' { + + $orderListInputObject = @{ + ResourcesToOrder = @( + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default' + type = 'Microsoft.Storage/storageAccounts/blobServices' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet' + type = 'Microsoft.Network/virtualNetworks/subnets' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa' + type = 'Microsoft.Storage/storageAccounts' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet' + type = 'Microsoft.Network/virtualNetworks' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2' + type = 'Microsoft.Authorization/roleAssignments' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg' + type = 'Microsoft.Network/networkSecurityGroups' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi' + type = 'Microsoft.ManagedIdentity/userAssignedIdentities' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4' + type = 'Microsoft.Authorization/roleAssignments' + } + @{ + resourceId = '/providers/Microsoft.Subscription/aliases/mySub' + type = 'Microsoft.Subscription/aliases' + } + @{ + resourceId = '/providers/Microsoft.Management/managementGroups/myMg/subscriptions/1-1-1-1-1' + type = 'bicep-lz-vending-automation-child/subscriptions' + } + ) + RemoveFirstSequence = @( + 'Microsoft.Authorization/roleAssignments', + 'Microsoft.ManagedIdentity/userAssignedIdentities' + 'Microsoft.Storage/storageAccounts' + ) + RemoveLastSequence = @() + } + $orderedList = Get-OrderedResourcesList @orderListInputObject + + $orderedList.resourceId | Should -Be @( + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2', + '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg', + '/providers/Microsoft.Subscription/aliases/mySub', + '/providers/Microsoft.Management/managementGroups/myMg/subscriptions/1-1-1-1-1' + ) + } + It 'Remove last sequence should be as expected' { + + $orderListInputObject = @{ + ResourcesToOrder = @( + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default' + type = 'Microsoft.Storage/storageAccounts/blobServices' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet' + type = 'Microsoft.Network/virtualNetworks/subnets' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa' + type = 'Microsoft.Storage/storageAccounts' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet' + type = 'Microsoft.Network/virtualNetworks' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2' + type = 'Microsoft.Authorization/roleAssignments' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg' + type = 'Microsoft.Network/networkSecurityGroups' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi' + type = 'Microsoft.ManagedIdentity/userAssignedIdentities' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4' + type = 'Microsoft.Authorization/roleAssignments' + } + @{ + resourceId = '/providers/Microsoft.Subscription/aliases/mySub' + type = 'Microsoft.Subscription/aliases' + } + @{ + resourceId = '/providers/Microsoft.Management/managementGroups/myMg/subscriptions/1-1-1-1-1' + type = 'bicep-lz-vending-automation-child/subscriptions' + } + ) + RemoveFirstSequence = @() + RemoveLastSequence = @( + 'Microsoft.Authorization/roleAssignments', + 'Microsoft.ManagedIdentity/userAssignedIdentities' + 'Microsoft.Storage/storageAccounts', + 'Microsoft.Subscription/aliases' + ) + } + $orderedList = Get-OrderedResourcesList @orderListInputObject + + $orderedList.resourceId | Should -Be @( + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg', + '/providers/Microsoft.Management/managementGroups/myMg/subscriptions/1-1-1-1-1', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2', + '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa' + '/providers/Microsoft.Subscription/aliases/mySub' + ) + } + + It 'Remove sequence should be as expected if both first & last priorities are provided' { + + $orderListInputObject = @{ + ResourcesToOrder = @( + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default' + type = 'Microsoft.Storage/storageAccounts/blobServices' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet' + type = 'Microsoft.Network/virtualNetworks/subnets' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa' + type = 'Microsoft.Storage/storageAccounts' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet' + type = 'Microsoft.Network/virtualNetworks' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2' + type = 'Microsoft.Authorization/roleAssignments' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg' + type = 'Microsoft.Network/networkSecurityGroups' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi' + type = 'Microsoft.ManagedIdentity/userAssignedIdentities' + } + @{ + resourceId = '/providers/Microsoft.Subscription/aliases/mySub' + type = 'Microsoft.Subscription/aliases' + } + @{ + resourceId = '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4' + type = 'Microsoft.Authorization/roleAssignments' + } + ) + RemoveFirstSequence = @( + 'Microsoft.Authorization/roleAssignments', + 'Microsoft.ManagedIdentity/userAssignedIdentities' + ) + RemoveLastSequence = @( + 'Microsoft.Storage/storageAccounts/blobServices', + 'Microsoft.Storage/storageAccounts' + 'Microsoft.Subscription/aliases' + ) + } + $orderedList = Get-OrderedResourcesList @orderListInputObject + + $orderedList.resourceId | Should -Be @( + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Authorization/roleAssignments/2-2-2-2-2', + '/subscriptions/1-1-1-1-1/providers/Microsoft.Authorization/roleAssignments/4-4-4-4-4', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myMsi', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/myVnet', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Network/networkSecurityGroups/myNsg', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa/blobServices/default', + '/subscriptions/1-1-1-1-1/resourceGroups/my-rg/providers/Microsoft.Storage/storageAccounts/mySa', + '/providers/Microsoft.Subscription/aliases/mySub' + ) + } +} diff --git a/avm/utilities/tools/Set-AVMModule.ps1 b/avm/utilities/tools/Set-AVMModule.ps1 index a583cc9995..761715d206 100644 --- a/avm/utilities/tools/Set-AVMModule.ps1 +++ b/avm/utilities/tools/Set-AVMModule.ps1 @@ -114,24 +114,29 @@ function Set-AVMModule { # Get latest release from Azure/Bicep repository # ---------------------------------------------- $latestReleaseUrl = 'https://api.github.com/repos/azure/bicep/releases/latest' - - $latestReleaseObject = Invoke-RestMethod -Uri $latestReleaseUrl -Method 'GET' - $releaseTag = $latestReleaseObject.tag_name - $latestReleaseVersion = [version]($releaseTag -replace 'v', '') - $latestReleaseUrl = $latestReleaseObject.html_url - - # Get latest installed Bicep CLI version - # -------------------------------------- - $latestInstalledVersionOutput = bicep --version - - if ($latestInstalledVersionOutput -match ' ([0-9]+\.[0-9]+\.[0-9]+) ') { - $latestInstalledVersion = [version]$matches[1] + try { + $latestReleaseObject = Invoke-RestMethod -Uri $latestReleaseUrl -Method 'GET' + } catch { + Write-Warning "Skipping Bicep version check as url [$latestReleaseUrl] did not return a response." } + if ($latestReleaseObject) { + # Only run if connected to the internet / url returns a response + $releaseTag = $latestReleaseObject.tag_name + $latestReleaseVersion = [version]($releaseTag -replace 'v', '') + $latestReleaseUrl = $latestReleaseObject.html_url + + # Get latest installed Bicep CLI version + # -------------------------------------- + $latestInstalledVersionOutput = bicep --version + + if ($latestInstalledVersionOutput -match ' ([0-9]+\.[0-9]+\.[0-9]+) ') { + $latestInstalledVersion = [version]$matches[1] + } - # Compare the versions - # -------------------- - if ($latestInstalledVersion -ne $latestReleaseVersion) { - Write-Warning """ + # Compare the versions + # -------------------- + if ($latestInstalledVersion -ne $latestReleaseVersion) { + Write-Warning """ You're not using the latest available Bicep CLI version [$latestReleaseVersion] but [$latestInstalledVersion]. You can find the latest release at: $latestReleaseUrl. @@ -140,10 +145,10 @@ For other OSs, please refer to the Bicep documentation (https://learn.microsoft. Note: The 'Bicep CLI' version (bicep --version) is not the same as the 'Azure CLI Bicep extension' version (az bicep version). """ - } else { - Write-Verbose "You're using the latest available Bicep CLI version [$latestInstalledVersion]." + } else { + Write-Verbose "You're using the latest available Bicep CLI version [$latestInstalledVersion]." + } } - } # Load recurring information we'll need for the modules @@ -160,10 +165,12 @@ Note: The 'Bicep CLI' version (bicep --version) is not the same as the 'Azure CL if (($rawResponse.Headers['Content-Type'] | Out-String) -like '*text/plain*') { $TelemetryFileContent = $rawResponse.Content -split '\n' } else { - throw "Failed to telemetry information from [$telemetryUrl]." # Incorrect Url (e.g., points to HTML) + Write-Warning "Failed to fetch telemetry information from [$telemetryUrl]. NOTE: You should re-run the script again at a later stage to ensure all data is collected and the readme correctly populated." # Incorrect Url (e.g., points to HTML) + $TelemetryFileContent = $null } } catch { - throw "Failed to telemetry information from [$telemetryUrl]." # Invalid url + Write-Warning "Failed to fetch telemetry information from [$telemetryUrl]. NOTE: You should re-run the script again at a later stage to ensure all data is collected and the readme correctly populated." # Invalid url + $TelemetryFileContent = $null } # create reference as it must be loaded in the thread to work diff --git a/avm/utilities/tools/helper/Set-ModuleFileAndFolderSetup.ps1 b/avm/utilities/tools/helper/Set-ModuleFileAndFolderSetup.ps1 index ff345c8e04..f9ead6bfb2 100644 --- a/avm/utilities/tools/helper/Set-ModuleFileAndFolderSetup.ps1 +++ b/avm/utilities/tools/helper/Set-ModuleFileAndFolderSetup.ps1 @@ -46,17 +46,14 @@ function Set-ModuleFileAndFolderSetup { ) if ([String]::IsNullOrEmpty($CurrentLevelFolderPath)) { - # Extract resource type identifier - $resourceTypeIdentifier = ($FullModuleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] # \ + # Extract path elements + $repoRoot, $moduleType, $resourceTypeIdentifier = $FullModuleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}' # .*/bicep-registry-modules, res|ptn, / # Split resource type identifier into components - $providerNamespace, $resourceType, $childResourceType = $resourceTypeIdentifier -split '[\/|\\]', 3 - - # Construct the root module path up to the providerNamespace (excluding resourceType and childResourceType) - $avmModuleRoot = ($FullModuleFolderPath -split [regex]::Escape("\$providerNamespace\$resourceType"))[0] + "\$providerNamespace" + $providerNamespace, $resourceType, $childResourceType = $resourceTypeIdentifier -split '[\/|\\]', 3 # , , # Join the required path to get up to the resource type folder - $CurrentLevelFolderPath = Join-Path -Path $avmModuleRoot -ChildPath $resourceType + $CurrentLevelFolderPath = Join-Path $repoRoot 'avm' $moduleType $providerNamespace $resourceType } # Collect data